aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.rootkeys48
-rw-r--r--BitKeeper/etc/logging_ok1
-rw-r--r--xen/common/lib.c3
-rw-r--r--xen/common/string.c535
-rw-r--r--xen/drivers/scsi/Makefile4
-rw-r--r--xen/drivers/scsi/aic7xxx/Makefile69
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7770.c369
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7770_osm.c150
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx.h1297
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx.reg3716
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx.seq1723
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_core.c8344
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_host.h95
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_inline.h997
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_osm.c2927
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_osm.h1272
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_osm_pci.c428
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_pci.c792
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_proc.c365
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_reg.h1357
-rw-r--r--xen/drivers/scsi/aic7xxx/aic79xx_seq.h824
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx.h1245
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx.reg1588
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx.seq2365
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.c306
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.h102
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_core.c7127
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_host.h103
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_inline.h563
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_osm.c3071
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_osm.h1199
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c416
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_pci.c2277
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_proc.c405
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_reg.h722
-rw-r--r--xen/drivers/scsi/aic7xxx/aic7xxx_seq.h1301
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/Makefile65
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/aicasm.c809
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/aicasm.h93
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y1858
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h131
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y164
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l154
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l594
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c500
-rw-r--r--xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h202
-rw-r--r--xen/drivers/scsi/aic7xxx/cam.h138
-rw-r--r--xen/drivers/scsi/aic7xxx/queue.h501
-rw-r--r--xen/drivers/scsi/aic7xxx/scsi_iu.h31
-rw-r--r--xen/drivers/scsi/aic7xxx/scsi_message.h70
-rw-r--r--xen/include/asm-i386/string.h500
-rw-r--r--xen/include/xeno/lib.h6
-rw-r--r--xen/include/xeno/string.h90
53 files changed, 54010 insertions, 2 deletions
diff --git a/.rootkeys b/.rootkeys
index 99803cc8e7..56195deb5c 100644
--- a/.rootkeys
+++ b/.rootkeys
@@ -246,6 +246,7 @@
3e397e6619PgAfBbw2XFbXkewvUWgw xen/common/schedule.c
3ddb79bdB9RNMnkQnUyZ5C9hhMSQQw xen/common/slab.c
3ddb79bd0gVQYmL2zvuJnldvD0AGxQ xen/common/softirq.c
+3e7f358awXBC3Vw-wFRwPw18qL1khg xen/common/string.c
3ddb79bdQqFHtHRGEO2dsxGgo6eAhw xen/common/timer.c
3ddb79bd3zgV33PHdt-cgh3sxcb1hw xen/common/vsprintf.c
3ddb79c0ppNeJtjC4va8j41ADCnchA xen/drivers/Makefile
@@ -316,6 +317,51 @@
3e564137taxQbEVa39h3mEVLFxRydQ xen/drivers/scsi/aacraid/linit.c
3e564137vmALxpfK7vNINqEklSmQ1A xen/drivers/scsi/aacraid/rx.c
3e564137EuYUJgvqnOunPERWxnp_mw xen/drivers/scsi/aacraid/sa.c
+3e7f358amV8HBe1Cyges2wsBHfh-cw xen/drivers/scsi/aic7xxx/Makefile
+3e7f358al_wJbBvERr7NzW5mLuTRpQ xen/drivers/scsi/aic7xxx/aic7770.c
+3e7f358aCCSVBMcmsh64S7CCVIgVQg xen/drivers/scsi/aic7xxx/aic7770_osm.c
+3e7f358aLNpSseMxk_DY6a-Ln9OyGg xen/drivers/scsi/aic7xxx/aic79xx.h
+3e7f358ad7OR21rmyDNJfKyE1X1sxg xen/drivers/scsi/aic7xxx/aic79xx.reg
+3e7f358a9Z39hG2enFVrNWjizvwthg xen/drivers/scsi/aic7xxx/aic79xx.seq
+3e7f358aKzcs8L8XYcLD0bF8FrmtVg xen/drivers/scsi/aic7xxx/aic79xx_core.c
+3e7f358ayrUAqJ9b8GxdRJQr5Po1zg xen/drivers/scsi/aic7xxx/aic79xx_host.h
+3e7f358aVDB0b6qAf__XSDPubVM1RA xen/drivers/scsi/aic7xxx/aic79xx_inline.h
+3e7f358aQFjWLEqUzd98Gy8z86vk2g xen/drivers/scsi/aic7xxx/aic79xx_osm.c
+3e7f358adsO2cSMKATR_r9hPZ9KCUg xen/drivers/scsi/aic7xxx/aic79xx_osm.h
+3e7f358avC9liKFAUFFRyWlVIIGZ2g xen/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
+3e7f358aQ0ZpnmwtiT16raQb1QSGKQ xen/drivers/scsi/aic7xxx/aic79xx_pci.c
+3e7f358af6BTct89-TCB31Rtw8NzQg xen/drivers/scsi/aic7xxx/aic79xx_proc.c
+3e7f358aK54-KV7Wj014Yf5FnWo2VQ xen/drivers/scsi/aic7xxx/aic79xx_reg.h
+3e7f358aZiCJAqEBttlmEDCMFcM-aA xen/drivers/scsi/aic7xxx/aic79xx_seq.h
+3e7f358aPZhHGnMNAM3YCQcBIAnCfw xen/drivers/scsi/aic7xxx/aic7xxx.h
+3e7f358aZRO8yoMXVLyDUyoFhsFnBw xen/drivers/scsi/aic7xxx/aic7xxx.reg
+3e7f358aADKO4MLsWISX0-d2hpru0g xen/drivers/scsi/aic7xxx/aic7xxx.seq
+3e7f358aB5x1ljQE7yWp63nvcfp4Mg xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
+3e7f358aDCmrU2Q35Ay765z7gWTSmQ xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
+3e7f358aXOqteLTeEH8XOlAeLPbXuQ xen/drivers/scsi/aic7xxx/aic7xxx_core.c
+3e7f358adzSPC6RFtP4NyHB2PoT-Ew xen/drivers/scsi/aic7xxx/aic7xxx_host.h
+3e7f358asCAiwyDZ1fBaiIxABh30BA xen/drivers/scsi/aic7xxx/aic7xxx_inline.h
+3e7f358aDwj6BY9tJ4spPuZnQqBiew xen/drivers/scsi/aic7xxx/aic7xxx_osm.c
+3e7f358anOWiWoFl7baNMWjqsPiYzg xen/drivers/scsi/aic7xxx/aic7xxx_osm.h
+3e7f358a1sRjg-XkczIXH7dUX8K1ZA xen/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
+3e7f358aj6uST9miAVmB9WNLqAGkCA xen/drivers/scsi/aic7xxx/aic7xxx_pci.c
+3e7f358aCwRu3fI3NP7MQsLRpWV73w xen/drivers/scsi/aic7xxx/aic7xxx_proc.c
+3e7f358aW8xbofV6VW2L8FPphmAXvg xen/drivers/scsi/aic7xxx/aic7xxx_reg.h
+3e7f358ayQSlIZBIAGWYXBM5dtAFrA xen/drivers/scsi/aic7xxx/aic7xxx_seq.h
+3e7f358acS9V8TbKLuyxvw-0SlgYmQ xen/drivers/scsi/aic7xxx/aicasm/Makefile
+3e7f358arkcPw6k1oC2Wrp5TXFbovg xen/drivers/scsi/aic7xxx/aicasm/aicasm.c
+3e7f358aF_JURnGn6q5V951k1TTnLg xen/drivers/scsi/aic7xxx/aicasm/aicasm.h
+3e7f358a2aopdml6zsS1HF9oibF5RQ xen/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
+3e7f358a2JQOz8ASFfi2oV5-k127RA xen/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
+3e7f358a5kU_nwzMFh0xbG96yLgJKA xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
+3e7f358a4FjmeDBfwgynM1InqFQ2nA xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
+3e7f358aoXDwp6vozr1x2Fwvb7x5BQ xen/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
+3e7f358aUYJHOx790PiwQiJIlYECbA xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
+3e7f358aPn8RK1slOgqrXC12x0FlgQ xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
+3e7f358a6BujWXsqDmuts6N_A8dGhw xen/drivers/scsi/aic7xxx/cam.h
+3e7f358anzaPhN49caB9AqMVw0_XKQ xen/drivers/scsi/aic7xxx/queue.h
+3e7f358a3ggaLCNsnuArANSmNT1t2A xen/drivers/scsi/aic7xxx/scsi_iu.h
+3e7f358arKN4VFRDcEasaM6Wj_VwTg xen/drivers/scsi/aic7xxx/scsi_message.h
3e56411aOLzeD5CRbuXquOrX0lRijw xen/drivers/scsi/constants.c
3ddb79beXZxwKh7cGyPfr40bhDyRrA xen/drivers/scsi/constants.h
3e564120ZeinH9nf3IVSerB80T7dHg xen/drivers/scsi/hosts.c
@@ -375,6 +421,7 @@
3ddb79c3jn8ALV_S9W5aeTYUQRKBpg xen/include/asm-i386/smpboot.h
3ddb79c3e9DCEoR-WzNxcOQDzLu7BQ xen/include/asm-i386/softirq.h
3ddb79c3NiyQE2vQnyGiaBnNjBO1rA xen/include/asm-i386/spinlock.h
+3e7f358aG11EvMI9VJ4_9hD4LUO7rQ xen/include/asm-i386/string.h
3ddb79c3ezddh34MdelJpa5tNR00Dw xen/include/asm-i386/system.h
3e397e66xPNc8eaSqC9pPbyAtRGzHA xen/include/asm-i386/time.h
3e450943TfE-iovQIY_tMO_VdGsPhA xen/include/asm-i386/timex.h
@@ -453,6 +500,7 @@
3ddb79c1-yIt89RT02wIPp2xDR8YjQ xen/include/xeno/socket.h
3ddb79c2V2P9F2xMCzDJ9vbUofSg_Q xen/include/xeno/sockios.h
3ddb79c2iIcESrDAB8samy_yAh6olQ xen/include/xeno/spinlock.h
+3e7f358aMtFMUVvN_Zjg5qvEJIqEBA xen/include/xeno/string.h
3ddb79c0BnA20PbgmuMPSGIBljNRQw xen/include/xeno/time.h
3ddb79c2HFkXuRxi1CriJtSFmY6Ybw xen/include/xeno/timer.h
3ddb79c2_m8lT9jDKse_tePj7zcnNQ xen/include/xeno/timex.h
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index 80c0db8f9a..b6e8d009bf 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -5,6 +5,7 @@ akw27@plucky.localdomain
bd240@boulderdash.cl.cam.ac.uk
bd240@labyrinth.cl.cam.ac.uk
iap10@labyrinth.cl.cam.ac.uk
+jws@cairnwell.research
kaf24@labyrinth.cl.cam.ac.uk
kaf24@plym.cl.cam.ac.uk
kaf24@scramble.cl.cam.ac.uk
diff --git a/xen/common/lib.c b/xen/common/lib.c
index ae75196ffd..c871341b7f 100644
--- a/xen/common/lib.c
+++ b/xen/common/lib.c
@@ -2,6 +2,7 @@
#include <xeno/ctype.h>
#include <xeno/lib.h>
+#if 0 // jws - now in string.c, string.h, asm/string.h
int memcmp(const void * cs,const void * ct,size_t count)
{
const unsigned char *su1, *su2;
@@ -119,7 +120,7 @@ char * strstr(const char * s1,const char * s2)
}
return NULL;
}
-
+#endif
/* for inc/ctype.h */
unsigned char _ctype[] = {
diff --git a/xen/common/string.c b/xen/common/string.c
new file mode 100644
index 0000000000..2ff4efc426
--- /dev/null
+++ b/xen/common/string.c
@@ -0,0 +1,535 @@
+/*
+ * linux/lib/string.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * - Added strsep() which will replace strtok() soon (because strsep() is
+ * reentrant and should be faster). Use only strsep() in new code, please.
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+
+#ifndef __HAVE_ARCH_STRNICMP
+/**
+ * strnicmp - Case insensitive, length-limited string comparison
+ * @s1: One string
+ * @s2: The other string
+ * @len: the maximum number of characters to compare
+ */
+int strnicmp(const char *s1, const char *s2, size_t len)
+{
+ /* Yes, Virginia, it had better be unsigned */
+ unsigned char c1, c2;
+
+ c1 = 0; c2 = 0;
+ if (len) {
+ do {
+ c1 = *s1; c2 = *s2;
+ s1++; s2++;
+ if (!c1)
+ break;
+ if (!c2)
+ break;
+ if (c1 == c2)
+ continue;
+ c1 = tolower(c1);
+ c2 = tolower(c2);
+ if (c1 != c2)
+ break;
+ } while (--len);
+ }
+ return (int)c1 - (int)c2;
+}
+#endif
+
+char * ___strtok;
+
+#ifndef __HAVE_ARCH_STRCPY
+/**
+ * strcpy - Copy a %NUL terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ */
+char * strcpy(char * dest,const char *src)
+{
+ char *tmp = dest;
+
+ while ((*dest++ = *src++) != '\0')
+ /* nothing */;
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCPY
+/**
+ * strncpy - Copy a length-limited, %NUL-terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @count: The maximum number of bytes to copy
+ *
+ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
+ * However, the result is not %NUL-terminated if the source exceeds
+ * @count bytes.
+ */
+char * strncpy(char * dest,const char *src,size_t count)
+{
+ char *tmp = dest;
+
+ while (count-- && (*dest++ = *src++) != '\0')
+ /* nothing */;
+
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCAT
+/**
+ * strcat - Append one %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ */
+char * strcat(char * dest, const char * src)
+{
+ char *tmp = dest;
+
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++) != '\0')
+ ;
+
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCAT
+/**
+ * strncat - Append a length-limited, %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ * @count: The maximum numbers of bytes to copy
+ *
+ * Note that in contrast to strncpy, strncat ensures the result is
+ * terminated.
+ */
+char * strncat(char *dest, const char *src, size_t count)
+{
+ char *tmp = dest;
+
+ if (count) {
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++)) {
+ if (--count == 0) {
+ *dest = '\0';
+ break;
+ }
+ }
+ }
+
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCMP
+/**
+ * strcmp - Compare two strings
+ * @cs: One string
+ * @ct: Another string
+ */
+int strcmp(const char * cs,const char * ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ }
+
+ return __res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCMP
+/**
+ * strncmp - Compare two length-limited strings
+ * @cs: One string
+ * @ct: Another string
+ * @count: The maximum number of bytes to compare
+ */
+int strncmp(const char * cs,const char * ct,size_t count)
+{
+ register signed char __res = 0;
+
+ while (count) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ count--;
+ }
+
+ return __res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCHR
+/**
+ * strchr - Find the first occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strchr(const char * s, int c)
+{
+ for(; *s != (char) c; ++s)
+ if (*s == '\0')
+ return NULL;
+ return (char *) s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRRCHR
+/**
+ * strrchr - Find the last occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strrchr(const char * s, int c)
+{
+ const char *p = s + strlen(s);
+ do {
+ if (*p == (char)c)
+ return (char *)p;
+ } while (--p >= s);
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRLEN
+/**
+ * strlen - Find the length of a string
+ * @s: The string to be sized
+ */
+size_t strlen(const char * s)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNLEN
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+ const char *sc;
+
+ for (sc = s; count-- && *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSPN
+/**
+ * strspn - Calculate the length of the initial substring of @s which only
+ * contain letters in @accept
+ * @s: The string to be searched
+ * @accept: The string to search for
+ */
+size_t strspn(const char *s, const char *accept)
+{
+ const char *p;
+ const char *a;
+ size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p) {
+ for (a = accept; *a != '\0'; ++a) {
+ if (*p == *a)
+ break;
+ }
+ if (*a == '\0')
+ return count;
+ ++count;
+ }
+
+ return count;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRPBRK
+/**
+ * strpbrk - Find the first occurrence of a set of characters
+ * @cs: The string to be searched
+ * @ct: The characters to search for
+ */
+char * strpbrk(const char * cs,const char * ct)
+{
+ const char *sc1,*sc2;
+
+ for( sc1 = cs; *sc1 != '\0'; ++sc1) {
+ for( sc2 = ct; *sc2 != '\0'; ++sc2) {
+ if (*sc1 == *sc2)
+ return (char *) sc1;
+ }
+ }
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRTOK
+/**
+ * strtok - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * WARNING: strtok is deprecated, use strsep instead.
+ */
+char * strtok(char * s,const char * ct)
+{
+ char *sbegin, *send;
+
+ sbegin = s ? s : ___strtok;
+ if (!sbegin) {
+ return NULL;
+ }
+ sbegin += strspn(sbegin,ct);
+ if (*sbegin == '\0') {
+ ___strtok = NULL;
+ return( NULL );
+ }
+ send = strpbrk( sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ___strtok = send;
+ return (sbegin);
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSEP
+/**
+ * strsep - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * strsep() updates @s to point after the token, ready for the next call.
+ *
+ * It returns empty tokens, too, behaving exactly like the libc function
+ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
+ * Same semantics, slimmer shape. ;)
+ */
+char * strsep(char **s, const char *ct)
+{
+ char *sbegin = *s, *end;
+
+ if (sbegin == NULL)
+ return NULL;
+
+ end = strpbrk(sbegin, ct);
+ if (end)
+ *end++ = '\0';
+ *s = end;
+
+ return sbegin;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET
+/**
+ * memset - Fill a region of memory with the given value
+ * @s: Pointer to the start of the area.
+ * @c: The byte to fill the area with
+ * @count: The size of the area.
+ *
+ * Do not use memset() to access IO space, use memset_io() instead.
+ */
+void * memset(void * s,int c,size_t count)
+{
+ char *xs = (char *) s;
+
+ while (count--)
+ *xs++ = c;
+
+ return s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_BCOPY
+/**
+ * bcopy - Copy one area of memory to another
+ * @src: Where to copy from
+ * @dest: Where to copy to
+ * @count: The size of the area.
+ *
+ * Note that this is the same as memcpy(), with the arguments reversed.
+ * memcpy() is the standard, bcopy() is a legacy BSD function.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+char * bcopy(const char * src, char * dest, int count)
+{
+ char *tmp = dest;
+
+ while (count--)
+ *tmp++ = *src++;
+
+ return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCPY
+/**
+ * memcpy - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+void * memcpy(void * dest,const void *src,size_t count)
+{
+ char *tmp = (char *) dest, *s = (char *) src;
+
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMMOVE
+/**
+ * memmove - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * Unlike memcpy(), memmove() copes with overlapping areas.
+ */
+void * memmove(void * dest,const void *src,size_t count)
+{
+ char *tmp, *s;
+
+ if (dest <= src) {
+ tmp = (char *) dest;
+ s = (char *) src;
+ while (count--)
+ *tmp++ = *s++;
+ }
+ else {
+ tmp = (char *) dest + count;
+ s = (char *) src + count;
+ while (count--)
+ *--tmp = *--s;
+ }
+
+ return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCMP
+/**
+ * memcmp - Compare two areas of memory
+ * @cs: One area of memory
+ * @ct: Another area of memory
+ * @count: The size of the area.
+ */
+/*
+int memcmp(const void * cs,const void * ct,size_t count)
+{
+ const unsigned char *su1, *su2;
+ int res = 0;
+
+ for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
+ if ((res = *su1 - *su2) != 0)
+ break;
+ return res;
+}
+*/
+#endif
+
+#ifndef __HAVE_ARCH_MEMSCAN
+/**
+ * memscan - Find a character in an area of memory.
+ * @addr: The memory area
+ * @c: The byte to search for
+ * @size: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or 1 byte past
+ * the area if @c is not found
+ */
+void * memscan(void * addr, int c, size_t size)
+{
+ unsigned char * p = (unsigned char *) addr;
+
+ while (size) {
+ if (*p == c)
+ return (void *) p;
+ p++;
+ size--;
+ }
+ return (void *) p;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSTR
+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char * strstr(const char * s1,const char * s2)
+{
+ int l1, l2;
+
+ l2 = strlen(s2);
+ if (!l2)
+ return (char *) s1;
+ l1 = strlen(s1);
+ while (l1 >= l2) {
+ l1--;
+ if (!memcmp(s1,s2,l2))
+ return (char *) s1;
+ s1++;
+ }
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCHR
+/**
+ * memchr - Find a character in an area of memory.
+ * @s: The memory area
+ * @c: The byte to search for
+ * @n: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or %NULL
+ * if @c is not found
+ */
+void *memchr(const void *s, int c, size_t n)
+{
+ const unsigned char *p = s;
+ while (n-- != 0) {
+ if ((unsigned char)c == *p++) {
+ return (void *)(p-1);
+ }
+ }
+ return NULL;
+}
+
+#endif
diff --git a/xen/drivers/scsi/Makefile b/xen/drivers/scsi/Makefile
index 5b480bdf53..5753581b47 100644
--- a/xen/drivers/scsi/Makefile
+++ b/xen/drivers/scsi/Makefile
@@ -3,9 +3,11 @@ include $(BASEDIR)/Rules.mk
default: $(OBJS)
$(MAKE) -C aacraid
- $(LD) -r -o driver.o $(OBJS) aacraid/aacraid.o
+ $(MAKE) -C aic7xxx
+ $(LD) -r -o driver.o $(OBJS) aacraid/aacraid.o aic7xxx/aic7xxx.o
# $(LD) -r -o driver.o $(OBJS)
clean:
$(MAKE) -C aacraid clean
+ $(MAKE) -C aic7xxx clean
rm -f *.o *~ core
diff --git a/xen/drivers/scsi/aic7xxx/Makefile b/xen/drivers/scsi/aic7xxx/Makefile
new file mode 100644
index 0000000000..333cea5f6a
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/Makefile
@@ -0,0 +1,69 @@
+#
+# drivers/scsi/aic7xxx/Makefile
+#
+# Makefile for the Linux aic7xxx SCSI driver.
+#
+
+O_TARGET := aic7xxx_drv.o
+
+include $(BASEDIR)/Rules.mk
+#CFLAGS += -I$(BASEDIR)/drivers/scsi
+
+#list-multi := aic7xxx.o aic79xx.o
+
+#obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx.o
+#ifeq ($(CONFIG_PCI),y)
+#obj-$(CONFIG_SCSI_AIC79XX) += aic79xx.o
+#endif
+
+#EXTRA_CFLAGS += -g
+
+# Platform Specific Files
+obj-aic7xxx = aic7xxx_osm.o aic7xxx_proc.o aic7770_osm.o
+#PCI Specific Platform Files
+#ifeq ($(CONFIG_PCI),y)
+obj-aic7xxx += aic7xxx_osm_pci.o
+#endif
+# Core Files
+obj-aic7xxx += aic7xxx_core.o aic7xxx_93cx6.o aic7770.o
+#PCI Specific Core Files
+#ifeq ($(CONFIG_PCI),y)
+obj-aic7xxx += aic7xxx_pci.o
+#endif
+
+# Platform Specific U320 Files
+#obj-aic79xx = aic79xx_osm.o
+#obj-aic79xx += aic79xx_proc.o
+#obj-aic79xx += aic79xx_osm_pci.o
+# Core Files
+#obj-aic79xx += aic79xx_core.o
+#obj-aic79xx += aic79xx_pci.o
+
+default: aic7xxx.o
+
+aic7xxx_core.o: aic7xxx_seq.h
+$(obj-aic7xxx): aic7xxx_reg.h
+aic7xxx.o: aic7xxx_seq.h aic7xxx_reg.h $(obj-aic7xxx)
+ $(LD) $(LD_RFLAG) -r -o $@ $(obj-aic7xxx)
+
+aic79xx_core.o: aic79xx_seq.h
+$(obj-aic79xx): aic79xx_reg.h
+aic79xx.o: aic79xx_seq.h aic79xx_reg.h $(obj-aic79xx)
+ $(LD) $(LD_RFLAG) -r -o $@ $(obj-aic79xx)
+
+#ifeq ($(CONFIG_AIC7XXX_BUILD_FIRMWARE),y)
+#aic7xxx_seq.h aic7xxx_reg.h: aic7xxx.seq aic7xxx.reg aicasm/aicasm
+# aicasm/aicasm -I. -r aic7xxx_reg.h -o aic7xxx_seq.h aic7xxx.seq
+#endif
+
+#ifeq ($(CONFIG_AIC79XX_BUILD_FIRMWARE),y)
+#aic79xx_seq.h aic79xx_reg.h: aic79xx.seq aic79xx.reg aicasm/aicasm
+# aicasm/aicasm -I. -r aic79xx_reg.h -o aic79xx_seq.h aic79xx.seq
+#endif
+
+#aicasm/aicasm: aicasm/*.[chyl]
+# $(MAKE) -C aicasm
+
+clean:
+ rm -f *.o *~ core
+
diff --git a/xen/drivers/scsi/aic7xxx/aic7770.c b/xen/drivers/scsi/aic7xxx/aic7770.c
new file mode 100644
index 0000000000..045f54c1e2
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7770.c
@@ -0,0 +1,369 @@
+/*
+ * Product specific probe and attach routines for:
+ * 27/284X and aic7770 motherboard SCSI controllers
+ *
+ * Copyright (c) 1994-1998, 2000, 2001 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7770.c#21 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7770.c,v 1.1 2000/09/16 20:02:27 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+#define ID_AIC7770 0x04907770
+#define ID_AHA_274x 0x04907771
+#define ID_AHA_284xB 0x04907756 /* BIOS enabled */
+#define ID_AHA_284x 0x04907757 /* BIOS disabled*/
+
+static int aha2840_load_seeprom(struct ahc_softc *ahc);
+static ahc_device_setup_t ahc_aic7770_VL_setup;
+static ahc_device_setup_t ahc_aic7770_EISA_setup;;
+static ahc_device_setup_t ahc_aic7770_setup;
+
+
+struct aic7770_identity aic7770_ident_table [] =
+{
+ {
+ ID_AHA_274x,
+ 0xFFFFFFFF,
+ "Adaptec 274X SCSI adapter",
+ ahc_aic7770_EISA_setup
+ },
+ {
+ ID_AHA_284xB,
+ 0xFFFFFFFE,
+ "Adaptec 284X SCSI adapter",
+ ahc_aic7770_VL_setup
+ },
+ /* Generic chip probes for devices we don't know 'exactly' */
+ {
+ ID_AIC7770,
+ 0xFFFFFFFF,
+ "Adaptec aic7770 SCSI adapter",
+ ahc_aic7770_EISA_setup
+ }
+};
+const int ahc_num_aic7770_devs = NUM_ELEMENTS(aic7770_ident_table);
+
+struct aic7770_identity *
+aic7770_find_device(uint32_t id)
+{
+ struct aic7770_identity *entry;
+ int i;
+
+ for (i = 0; i < ahc_num_aic7770_devs; i++) {
+ entry = &aic7770_ident_table[i];
+ if (entry->full_id == (id & entry->id_mask))
+ return (entry);
+ }
+ return (NULL);
+}
+
+int
+aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry, u_int io)
+{
+ u_long l;
+ u_long s;
+ int error;
+ int have_seeprom;
+ u_int hostconf;
+ u_int irq;
+ u_int intdef;
+
+ error = entry->setup(ahc);
+ have_seeprom = 0;
+ if (error != 0)
+ return (error);
+
+ error = aic7770_map_registers(ahc, io);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Before we continue probing the card, ensure that
+ * its interrupts are *disabled*. We don't want
+ * a misstep to hang the machine in an interrupt
+ * storm.
+ */
+ ahc_intr_enable(ahc, FALSE);
+
+ ahc->description = entry->name;
+ error = ahc_softc_init(ahc);
+
+ error = ahc_reset(ahc);
+ if (error != 0)
+ return (error);
+
+ /* Make sure we have a valid interrupt vector */
+ intdef = ahc_inb(ahc, INTDEF);
+ irq = intdef & VECTOR;
+ switch (irq) {
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ break;
+ default:
+ printf("aic7770_config: illegal irq setting %d\n", intdef);
+ return (ENXIO);
+ }
+
+ if ((intdef & EDGE_TRIG) != 0)
+ ahc->flags |= AHC_EDGE_INTERRUPT;
+
+ switch (ahc->chip & (AHC_EISA|AHC_VL)) {
+ case AHC_EISA:
+ {
+ u_int biosctrl;
+ u_int scsiconf;
+ u_int scsiconf1;
+
+ biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL);
+ scsiconf = ahc_inb(ahc, SCSICONF);
+ scsiconf1 = ahc_inb(ahc, SCSICONF + 1);
+
+ /* Get the primary channel information */
+ if ((biosctrl & CHANNEL_B_PRIMARY) != 0)
+ ahc->flags |= 1;
+
+ if ((biosctrl & BIOSMODE) == BIOSDISABLED) {
+ ahc->flags |= AHC_USEDEFAULTS;
+ } else {
+ if ((ahc->features & AHC_WIDE) != 0) {
+ ahc->our_id = scsiconf1 & HWSCSIID;
+ if (scsiconf & TERM_ENB)
+ ahc->flags |= AHC_TERM_ENB_A;
+ } else {
+ ahc->our_id = scsiconf & HSCSIID;
+ ahc->our_id_b = scsiconf1 & HSCSIID;
+ if (scsiconf & TERM_ENB)
+ ahc->flags |= AHC_TERM_ENB_A;
+ if (scsiconf1 & TERM_ENB)
+ ahc->flags |= AHC_TERM_ENB_B;
+ }
+ }
+ if ((ahc_inb(ahc, HA_274_BIOSGLOBAL) & HA_274_EXTENDED_TRANS))
+ ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B;
+ break;
+ }
+ case AHC_VL:
+ {
+ have_seeprom = aha2840_load_seeprom(ahc);
+ break;
+ }
+ default:
+ break;
+ }
+ if (have_seeprom == 0) {
+ free(ahc->seep_config, M_DEVBUF);
+ ahc->seep_config = NULL;
+ }
+
+ /*
+ * Ensure autoflush is enabled
+ */
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~AUTOFLUSHDIS);
+
+ /* Setup the FIFO threshold and the bus off time */
+ hostconf = ahc_inb(ahc, HOSTCONF);
+ ahc_outb(ahc, BUSSPD, hostconf & DFTHRSH);
+ ahc_outb(ahc, BUSTIME, (hostconf << 2) & BOFF);
+
+ /*
+ * Generic aic7xxx initialization.
+ */
+ error = ahc_init(ahc);
+ if (error != 0)
+ return (error);
+
+ error = aic7770_map_int(ahc, irq);
+ if (error != 0)
+ return (error);
+
+ ahc_list_lock(&l);
+ /*
+ * Link this softc in with all other ahc instances.
+ */
+ ahc_softc_insert(ahc);
+
+ /*
+ * Enable the board's BUS drivers
+ */
+ ahc_outb(ahc, BCTL, ENABLE);
+
+ /*
+ * Allow interrupts.
+ */
+ ahc_lock(ahc, &s);
+ ahc_intr_enable(ahc, TRUE);
+ ahc_unlock(ahc, &s);
+
+ ahc_list_unlock(&l);
+
+ return (0);
+}
+
+/*
+ * Read the 284x SEEPROM.
+ */
+static int
+aha2840_load_seeprom(struct ahc_softc *ahc)
+{
+ struct seeprom_descriptor sd;
+ struct seeprom_config *sc;
+ int have_seeprom;
+ uint8_t scsi_conf;
+
+ sd.sd_ahc = ahc;
+ sd.sd_control_offset = SEECTL_2840;
+ sd.sd_status_offset = STATUS_2840;
+ sd.sd_dataout_offset = STATUS_2840;
+ sd.sd_chip = C46;
+ sd.sd_MS = 0;
+ sd.sd_RDY = EEPROM_TF;
+ sd.sd_CS = CS_2840;
+ sd.sd_CK = CK_2840;
+ sd.sd_DO = DO_2840;
+ sd.sd_DI = DI_2840;
+ sc = ahc->seep_config;
+
+ if (bootverbose)
+ printf("%s: Reading SEEPROM...", ahc_name(ahc));
+ have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)&sc,
+ /*start_addr*/0, sizeof(sc)/2);
+
+ if (have_seeprom) {
+
+ if (ahc_verify_cksum(sc) == 0) {
+ if(bootverbose)
+ printf ("checksum error\n");
+ have_seeprom = 0;
+ } else if (bootverbose) {
+ printf("done.\n");
+ }
+ }
+
+ if (!have_seeprom) {
+ if (bootverbose)
+ printf("%s: No SEEPROM available\n", ahc_name(ahc));
+ ahc->flags |= AHC_USEDEFAULTS;
+ } else {
+ /*
+ * Put the data we've collected down into SRAM
+ * where ahc_init will find it.
+ */
+ int i;
+ int max_targ;
+ uint16_t discenable;
+
+ max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8;
+ discenable = 0;
+ for (i = 0; i < max_targ; i++){
+ uint8_t target_settings;
+
+ target_settings = (sc->device_flags[i] & CFXFER) << 4;
+ if (sc->device_flags[i] & CFSYNCH)
+ target_settings |= SOFS;
+ if (sc->device_flags[i] & CFWIDEB)
+ target_settings |= WIDEXFER;
+ if (sc->device_flags[i] & CFDISC)
+ discenable |= (0x01 << i);
+ ahc_outb(ahc, TARG_SCSIRATE + i, target_settings);
+ }
+ ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+ ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+
+ ahc->our_id = sc->brtime_id & CFSCSIID;
+
+ scsi_conf = (ahc->our_id & 0x7);
+ if (sc->adapter_control & CFSPARITY)
+ scsi_conf |= ENSPCHK;
+ if (sc->adapter_control & CFRESETB)
+ scsi_conf |= RESET_SCSI;
+
+ if (sc->bios_control & CF284XEXTEND)
+ ahc->flags |= AHC_EXTENDED_TRANS_A;
+ /* Set SCSICONF info */
+ ahc_outb(ahc, SCSICONF, scsi_conf);
+
+ if (sc->adapter_control & CF284XSTERM)
+ ahc->flags |= AHC_TERM_ENB_A;
+ }
+ return (have_seeprom);
+}
+
+static int
+ahc_aic7770_VL_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7770_setup(ahc);
+ ahc->chip |= AHC_VL;
+ return (error);
+}
+
+static int
+ahc_aic7770_EISA_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7770_setup(ahc);
+ ahc->chip |= AHC_EISA;
+ return (error);
+}
+
+static int
+ahc_aic7770_setup(struct ahc_softc *ahc)
+{
+ ahc->channel = 'A';
+ ahc->channel_b = 'B';
+ ahc->chip = AHC_AIC7770;
+ ahc->features = AHC_AIC7770_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
+ ahc->flags |= AHC_PAGESCBS;
+ return (0);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7770_osm.c b/xen/drivers/scsi/aic7xxx/aic7770_osm.c
new file mode 100644
index 0000000000..80d25528e9
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7770_osm.c
@@ -0,0 +1,150 @@
+/*
+ * Linux driver attachment glue for aic7770 based controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7770_osm.c#11 $
+ */
+
+#include "aic7xxx_osm.h"
+
+#define MINSLOT 1
+#define NUMSLOTS 16
+#define IDOFFSET 0x80
+
+int
+aic7770_linux_probe(Scsi_Host_Template *template)
+{
+#if defined(__i386__) || defined(__alpha__)
+ struct aic7770_identity *entry;
+ struct ahc_softc *ahc;
+ int i, slot;
+ int eisaBase;
+ int found;
+
+ eisaBase = 0x1000 + AHC_EISA_SLOT_OFFSET;
+ found = 0;
+ for (slot = 1; slot < NUMSLOTS; eisaBase+=0x1000, slot++) {
+ uint32_t eisa_id;
+ size_t id_size;
+
+ if (check_region(eisaBase, AHC_EISA_IOSIZE) != 0)
+ continue;
+
+ eisa_id = 0;
+ id_size = sizeof(eisa_id);
+ for (i = 0; i < 4; i++) {
+ /* VLcards require priming*/
+ outb(0x80 + i, eisaBase + IDOFFSET);
+ eisa_id |= inb(eisaBase + IDOFFSET + i)
+ << ((id_size-i-1) * 8);
+ }
+ if (eisa_id & 0x80000000)
+ continue; /* no EISA card in slot */
+
+ entry = aic7770_find_device(eisa_id);
+ if (entry != NULL) {
+ char buf[80];
+ char *name;
+ int error;
+
+ /*
+ * Allocate a softc for this card and
+ * set it up for attachment by our
+ * common detect routine.
+ */
+ sprintf(buf, "ahc_eisa:%d", slot);
+ name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (name == NULL)
+ break;
+ strcpy(name, buf);
+ ahc = ahc_alloc(template, name);
+ if (ahc == NULL) {
+ /*
+ * If we can't allocate this one,
+ * chances are we won't be able to
+ * allocate future card structures.
+ */
+ break;
+ }
+ error = aic7770_config(ahc, entry, eisaBase);
+ if (error != 0) {
+ ahc->bsh.ioport = 0;
+ ahc_free(ahc);
+ continue;
+ }
+ found++;
+ }
+ }
+ return (found);
+#else
+ return (0);
+#endif
+}
+
+int
+aic7770_map_registers(struct ahc_softc *ahc, u_int port)
+{
+ /*
+ * Lock out other contenders for our i/o space.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+ request_region(port, AHC_EISA_IOSIZE, "aic7xxx");
+#else
+ if (request_region(port, AHC_EISA_IOSIZE, "aic7xxx") == 0)
+ return (ENOMEM);
+#endif
+ ahc->tag = BUS_SPACE_PIO;
+ ahc->bsh.ioport = port;
+ return (0);
+}
+
+int
+aic7770_map_int(struct ahc_softc *ahc, u_int irq)
+{
+ int error;
+ int shared;
+
+ shared = 0;
+ if ((ahc->flags & AHC_EDGE_INTERRUPT) == 0)
+ shared = SA_SHIRQ;
+
+ error = request_irq(irq, ahc_linux_isr, shared, "aic7xxx", ahc);
+ if (error == 0)
+ ahc->platform_data->irq = irq;
+
+ return (-error);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx.h b/xen/drivers/scsi/aic7xxx/aic79xx.h
new file mode 100644
index 0000000000..b4a8abb226
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx.h
@@ -0,0 +1,1297 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.h#46 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC79XX_H_
+#define _AIC79XX_H_
+
+/* Register Definitions */
+#include "aic79xx_reg.h"
+
+/************************* Forward Declarations *******************************/
+struct ahd_platform_data;
+struct scb_platform_data;
+
+/****************************** Useful Macros *********************************/
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD (~0)
+#define SCB_LIST_NULL 0xFF00
+#define SCB_LIST_NULL_LE (ahd_htole16(SCB_LIST_NULL))
+#define SCBID_IS_NULL(scbid) (((scbid) & 0xFF00 ) == SCB_LIST_NULL)
+
+#define SCSIID_TARGET(ahd, scsiid) \
+ (((scsiid) & TID) >> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid) \
+ ((scsiid) & OID)
+#define SCSIID_CHANNEL(ahd, scsiid) ('A')
+#define SCB_IS_SCSIBUS_B(ahd, scb) (0)
+#define SCB_GET_OUR_ID(scb) \
+ SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define SCB_GET_TARGET(ahd, scb) \
+ SCSIID_TARGET((ahd), (scb)->hscb->scsiid)
+#define SCB_GET_CHANNEL(ahd, scb) \
+ SCSIID_CHANNEL(ahd, (scb)->hscb->scsiid)
+#define SCB_GET_LUN(scb) \
+ ((scb)->hscb->lun)
+#define SCB_GET_TARGET_OFFSET(ahd, scb) \
+ SCB_GET_TARGET(ahd, scb)
+#define SCB_GET_TARGET_MASK(ahd, scb) \
+ (0x01 << (SCB_GET_TARGET_OFFSET(ahd, scb)))
+/*
+ * TCLs have the following format: TTTTLLLLLLLL
+ */
+#define TCL_TARGET_OFFSET(tcl) \
+ ((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+ (tcl & (AHD_NUM_LUNS_NONPKT - 1))
+#define BUILD_TCL(scsiid, lun) \
+ ((lun) | (((scsiid) & TID) << 4))
+#define BUILD_TCL_RAW(target, channel, lun) \
+ ((lun) | ((target) << 8))
+
+#define SCB_GET_TAG(scb) \
+ ahd_le16toh(scb->hscb->tag)
+
+#ifndef AHD_TARGET_MODE
+#undef AHD_TMODE_ENABLE
+#define AHD_TMODE_ENABLE 0
+#endif
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHD_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in non-packetized transfers.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but until we see a need to support that many, we support 256.
+ */
+#define AHD_NUM_LUNS_NONPKT 64
+#define AHD_NUM_LUNS 256
+
+/*
+ * The maximum transfer per S/G segment.
+ */
+#define AHD_MAXTRANSFER_SIZE 0x00ffffff /* limited by 24bit counter */
+
+/*
+ * The maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound. Due to software design,
+ * we may not be able to use this number.
+ */
+#define AHD_SCB_MAX 512
+
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information.
+ * We are limited to 510 because:
+ * 1) SCB storage space holds us to at most 512.
+ * 2) Our input queue scheme requires one SCB to always be reserved
+ * in advance of queuing any SCBs. This takes us down to 511.
+ * 3) To handle our output queue correctly on machines that only
+ * support 32bit stores, we must clear the array 4 bytes at a
+ * time. To avoid colliding with a DMA write from the sequencer,
+ * we must be sure that 2, 16bit slots are empty when we write to
+ * clear the queue. This restricts us to only 511 SCBs: 1 that
+ * just completed and the known additional empty slot in the queue
+ * that precedes it.
+#define AHD_MAX_QUEUE 510
+ */
+#define AHD_MAX_QUEUE 255
+
+/*
+ * Define the size of our QIN and QOUT FIFOs. They must be a power of 2
+ * in size and accomodate as many transactions as can be queued concurrently.
+ */
+#define AHD_QIN_SIZE 512
+#define AHD_QOUT_SIZE 512
+
+#define AHD_QIN_WRAP(x) ((x) & (AHD_QIN_SIZE-1))
+#define AHD_QOUT_WRAP(x) ((x) & (AHD_QOUT_SIZE-1))
+
+/*
+ * The maximum amount of SCB storage we allocate in host memory. This
+ * number should reflect the 1 additional SCB we require to handle our
+ * qinfifo mechanism.
+ */
+#define AHD_SCB_MAX_ALLOC (AHD_MAX_QUEUE+1)
+
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHD_TMODE_CMDS 256
+
+/* Reset line assertion time in us */
+#define AHD_BUSRESET_DELAY 250
+
+/******************* Chip Characteristics/Operating Settings *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
+typedef enum {
+ AHD_NONE = 0x0000,
+ AHD_CHIPID_MASK = 0x00FF,
+ AHD_AIC7901 = 0x0001,
+ AHD_AIC7902 = 0x0002,
+ AHD_PCI = 0x0100, /* Bus type PCI */
+ AHD_PCIX = 0x0200, /* Bus type PCIX */
+ AHD_BUS_MASK = 0x0F00
+} ahd_chip;
+
+/*
+ * Features available in each chip type.
+ */
+typedef enum {
+ AHD_FENONE = 0x00000,
+ AHD_WIDE = 0x00001, /* Wide Channel */
+ AHD_MULTI_FUNC = 0x00100, /* Multi-Function Twin Channel Device */
+ AHD_TARGETMODE = 0x01000, /* Has tested target mode support */
+ AHD_MULTIROLE = 0x02000, /* Space for two roles at a time */
+ AHD_REMOVABLE = 0x00000, /* Hot-Swap supported - None so far*/
+ AHD_AIC7901_FE = AHD_FENONE,
+ AHD_AIC7902_FE = AHD_MULTI_FUNC
+} ahd_feature;
+
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+ AHD_BUGNONE = 0x0000,
+ AHD_SENT_SCB_UPDATE_BUG = 0x0001,
+ AHD_ABORT_LQI_BUG = 0x0002,
+ AHD_PKT_BITBUCKET_BUG = 0x0004,
+ AHD_LONG_SETIMO_BUG = 0x0008,
+ AHD_NLQICRC_DELAYED_BUG = 0x0010,
+ AHD_SCSIRST_BUG = 0x0020,
+ AHD_PCIX_ARBITER_BUG = 0x0040,
+ AHD_PCIX_SPLIT_BUG = 0x0080,
+ AHD_PCIX_CHIPRST_BUG = 0x0100,
+ AHD_PCIX_MMAPIO_BUG = 0x0200,
+ /* Bug workarounds that can be disabled on non-PCIX busses. */
+ AHD_PCIX_BUG_MASK = AHD_PCIX_ARBITER_BUG
+ | AHD_PCIX_SPLIT_BUG
+ | AHD_PCIX_CHIPRST_BUG
+ | AHD_PCIX_MMAPIO_BUG,
+ AHD_LQO_ATNO_BUG = 0x0400,
+ AHD_AUTOFLUSH_BUG = 0x0800,
+ AHD_CLRLQO_AUTOCLR_BUG = 0x1000,
+ AHD_PKTIZED_STATUS_BUG = 0x2000
+} ahd_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
+typedef enum {
+ AHD_FNONE = 0x00000,
+ AHD_PRIMARY_CHANNEL = 0x00003,/*
+ * The channel that should
+ * be probed first.
+ */
+ AHD_USEDEFAULTS = 0x00004,/*
+ * For cards without an seeprom
+ * or a BIOS to initialize the chip's
+ * SRAM, we use the default target
+ * settings.
+ */
+ AHD_SEQUENCER_DEBUG = 0x00008,
+ AHD_RESET_BUS_A = 0x00010,
+ AHD_EXTENDED_TRANS_A = 0x00020,
+ AHD_TERM_ENB_A = 0x00040,
+ AHD_SPCHK_ENB_A = 0x00080,
+ AHD_STPWLEVEL_A = 0x00100,
+ AHD_INITIATORROLE = 0x00200,/*
+ * Allow initiator operations on
+ * this controller.
+ */
+ AHD_TARGETROLE = 0x00400,/*
+ * Allow target operations on this
+ * controller.
+ */
+ AHD_RESOURCE_SHORTAGE = 0x00800,
+ AHD_TQINFIFO_BLOCKED = 0x01000,/* Blocked waiting for ATIOs */
+ AHD_INT50_SPEEDFLEX = 0x02000,/*
+ * Internal 50pin connector
+ * sits behind an aic3860
+ */
+ AHD_BIOS_ENABLED = 0x04000,
+ AHD_ALL_INTERRUPTS = 0x08000,
+ AHD_39BIT_ADDRESSING = 0x10000,/* Use 39 bit addressing scheme. */
+ AHD_64BIT_ADDRESSING = 0x20000,/* Use 64 bit addressing scheme. */
+ AHD_CURRENT_SENSING = 0x40000,
+ AHD_SCB_CONFIG_USED = 0x80000,/* No SEEPROM but SCB had info. */
+ AHD_CPQ_BOARD = 0x100000
+} ahd_flag;
+
+/************************* Hardware SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory. The SCB
+ * consists of a "hardware SCB" mirroring the fields availible on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the cdb) to be executed. After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role. See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target. The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally.
+ */
+struct initiator_status {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sgptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* Standard SCSI status byte */
+};
+
+struct target_status {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sgptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* SCSI status to give to initiator */
+ uint8_t target_phases; /* Bitmap of phases to execute */
+ uint8_t data_phase; /* Data-In or Data-Out */
+ uint8_t initiator_tag; /* Initiator's transaction tag */
+};
+
+/*
+ * Initiator mode SCB shared data area.
+ * If the embedded CDB is 12 bytes or less, we embed
+ * the sense buffer address in the SCB. This allows
+ * us to retrieve sense information without interupting
+ * the host in packetized mode.
+ */
+typedef uint32_t sense_addr_t;
+#define MAX_CDB_LEN 16
+#define MAX_CDB_LEN_WITH_SENSE_ADDR (MAX_CDB_LEN - sizeof(sense_addr_t))
+union initiator_data {
+ uint64_t cdbptr;
+ uint8_t cdb[MAX_CDB_LEN];
+ struct {
+ uint8_t cdb[MAX_CDB_LEN_WITH_SENSE_ADDR];
+ sense_addr_t sense_addr;
+ } cdb_plus_saddr;
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+ uint32_t spare[2];
+ uint8_t scsi_status; /* SCSI status to give to initiator */
+ uint8_t target_phases; /* Bitmap of phases to execute */
+ uint8_t data_phase; /* Data-In or Data-Out */
+ uint8_t initiator_tag; /* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/ union {
+ union initiator_data idata;
+ struct target_data tdata;
+ struct initiator_status istatus;
+ struct target_status tstatus;
+ } shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer. The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag. Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields). If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur. Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields. After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced. After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set. If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete. If the
+ * sequencer determines that there is a residual in the tranfer, or
+ * there is non-zero status, it will set the SG_STATUS_VALID flag in
+ * sgptr and dma the scb back into host memory. To sumarize:
+ *
+ * Sequencer:
+ * o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ * or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ * o We are transfering the last segment if residual_datacnt has
+ * the SG_LAST_SEG flag set.
+ *
+ * Host:
+ * o A residual can only have occurred if a completed scb has the
+ * SG_STATUS_VALID flag set. Inspection of the SCSI status field,
+ * the residual_datacnt, and the residual_sgptr field will tell
+ * for sure.
+ *
+ * o residual_sgptr and sgptr refer to the "next" sg entry
+ * and so may point beyond the last valid sg entry for the
+ * transfer.
+ */
+#define SG_PTR_MASK 0xFFFFFFF8
+/*16*/ uint8_t cdb_len;
+/*17*/ uint8_t task_management;
+/*18*/ uint16_t tag;
+/*20*/ uint32_t next_hscb_busaddr;
+/*24*/ uint64_t dataptr;
+/*32*/ uint32_t datacnt; /* Byte 3 is spare. */
+/*36*/ uint32_t sgptr;
+/*40*/ uint8_t control; /* See SCB_CONTROL in aic79xx.reg for details */
+/*41*/ uint8_t scsiid; /*
+ * Selection out Id
+ * Our Id (bits 0-3) Their ID (bits 4-7)
+ */
+/*42*/ uint8_t lun;
+/*43*/ uint8_t task_attribute_nonpkt_tag;
+/*44*/ uint32_t hscb_busaddr;
+/******* Fields below are not Downloaded (Sequencer may use for scratch) ******/
+/*48*/ uint8_t spare[16];
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent. Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transfered to the controller.
+ * The aic7xxx chips only support a 24bit length. We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer. This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahd_dma_seg {
+ uint32_t addr;
+ uint32_t len;
+#define AHD_DMA_LAST_SEG 0x80000000
+#define AHD_SG_HIGH_ADDR_MASK 0x7F000000
+#define AHD_SG_LEN_MASK 0x00FFFFFF
+};
+
+struct ahd_dma64_seg {
+ uint64_t addr;
+ uint32_t len;
+ uint32_t pad;
+};
+
+struct map_node {
+ bus_dmamap_t dmamap;
+ bus_addr_t physaddr;
+ uint8_t *vaddr;
+ SLIST_ENTRY(map_node) links;
+};
+
+/*
+ * The current state of this SCB.
+ */
+typedef enum {
+ SCB_FREE = 0x0000,
+ SCB_TRANSMISSION_ERROR = 0x0001,/*
+ * We detected a parity or CRC
+ * error that has effected the
+ * payload of the command. This
+ * flag is checked when normal
+ * status is returned to catch
+ * the case of a target not
+ * responding to our attempt
+ * to report the error.
+ */
+ SCB_OTHERTCL_TIMEOUT = 0x0002,/*
+ * Another device was active
+ * during the first timeout for
+ * this SCB so we gave ourselves
+ * an additional timeout period
+ * in case it was hogging the
+ * bus.
+ */
+ SCB_DEVICE_RESET = 0x0004,
+ SCB_SENSE = 0x0008,
+ SCB_CDB32_PTR = 0x0010,
+ SCB_RECOVERY_SCB = 0x0020,
+ SCB_AUTO_NEGOTIATE = 0x0040,/* Negotiate to achieve goal. */
+ SCB_NEGOTIATE = 0x0080,/* Negotiation forced for command. */
+ SCB_ABORT = 0x0100,
+ SCB_UNTAGGEDQ = 0x0200,
+ SCB_ACTIVE = 0x0400,
+ SCB_TARGET_IMMEDIATE = 0x0800,
+ SCB_PACKETIZED = 0x1000,
+ SCB_EXPECT_PPR_BUSFREE = 0x2000,
+ SCB_PKT_SENSE = 0x4000,
+ SCB_CMDPHASE_ABORT = 0x8000
+} scb_flag;
+
+struct scb {
+ struct hardware_scb *hscb;
+ union {
+ SLIST_ENTRY(scb) sle;
+ TAILQ_ENTRY(scb) tqe;
+ } links;
+ LIST_ENTRY(scb) pending_links;
+ ahd_io_ctx_t io_ctx;
+ struct ahd_softc *ahd_softc;
+ scb_flag flags;
+#ifndef __linux__
+ bus_dmamap_t dmamap;
+#endif
+ struct scb_platform_data *platform_data;
+ struct map_node *hscb_map;
+ struct map_node *sg_map;
+ struct map_node *sense_map;
+ void *sg_list;
+ uint8_t *sense_data;
+ bus_addr_t sg_list_busaddr;
+ bus_addr_t sense_busaddr;
+ u_int sg_count;/* How full ahd_dma_seg is */
+};
+
+struct scb_data {
+ SLIST_HEAD(, scb) free_scbs; /*
+ * Pool of SCBs ready to be assigned
+ * commands to execute.
+ */
+ struct scb *scbindex[AHD_SCB_MAX];
+ /*
+ * Mapping from tag to SCB.
+ */
+ /*
+ * "Bus" addresses of our data structures.
+ */
+ bus_dma_tag_t hscb_dmat; /* dmat for our hardware SCB array */
+ bus_dma_tag_t sg_dmat; /* dmat for our sg segments */
+ bus_dma_tag_t sense_dmat; /* dmat for our sense buffers */
+ SLIST_HEAD(, map_node) hscb_maps;
+ SLIST_HEAD(, map_node) sg_maps;
+ SLIST_HEAD(, map_node) sense_maps;
+ int scbs_left; /* unallocated scbs in head map_node */
+ int sgs_left; /* unallocated sgs in head map_node */
+ int sense_left; /* unallocated sense in head map_node */
+ uint16_t numscbs;
+ uint16_t maxhscbs; /* Number of SCBs on the card */
+ uint8_t init_level; /*
+ * How far we've initialized
+ * this structure.
+ */
+};
+
+/************************ Target Mode Definitions *****************************/
+
+/*
+ * Connection desciptor for select-in requests in target mode.
+ */
+struct target_cmd {
+ uint8_t scsiid; /* Our ID and the initiator's ID */
+ uint8_t identify; /* Identify message */
+ uint8_t bytes[22]; /*
+ * Bytes contains any additional message
+ * bytes terminated by 0xFF. The remainder
+ * is the cdb to execute.
+ */
+ uint8_t cmd_valid; /*
+ * When a command is complete, the firmware
+ * will set cmd_valid to all bits set.
+ * After the host has seen the command,
+ * the bits are cleared. This allows us
+ * to just peek at host memory to determine
+ * if more work is complete. cmd_valid is on
+ * an 8 byte boundary to simplify setting
+ * it on aic7880 hardware which only has
+ * limited direct access to the DMA FIFO.
+ */
+ uint8_t pad[7];
+};
+
+/*
+ * Number of events we can buffer up if we run out
+ * of immediate notify ccbs.
+ */
+#define AHD_TMODE_EVENT_BUFFER_SIZE 8
+struct ahd_tmode_event {
+ uint8_t initiator_id;
+ uint8_t event_type; /* MSG type or EVENT_TYPE_BUS_RESET */
+#define EVENT_TYPE_BUS_RESET 0xFF
+ uint8_t event_arg;
+};
+
+/*
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it. Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
+ */
+#ifdef AHD_TARGET_MODE
+struct ahd_tmode_lstate {
+ struct cam_path *path;
+ struct ccb_hdr_slist accept_tios;
+ struct ccb_hdr_slist immed_notifies;
+ struct ahd_tmode_event event_buffer[AHD_TMODE_EVENT_BUFFER_SIZE];
+ uint8_t event_r_idx;
+ uint8_t event_w_idx;
+};
+#else
+struct ahd_tmode_lstate;
+#endif
+
+/******************** Transfer Negotiation Datastructures *********************/
+#define AHD_TRANS_CUR 0x01 /* Modify current neogtiation status */
+#define AHD_TRANS_ACTIVE 0x03 /* Assume this target is on the bus */
+#define AHD_TRANS_GOAL 0x04 /* Modify negotiation goal */
+#define AHD_TRANS_USER 0x08 /* Modify user negotiation settings */
+#define AHD_PERIOD_ASYNC 0xFF
+#define AHD_PERIOD_10MHz 0x19
+
+/*
+ * Transfer Negotiation Information.
+ */
+struct ahd_transinfo {
+ uint8_t protocol_version; /* SCSI Revision level */
+ uint8_t transport_version; /* SPI Revision level */
+ uint8_t width; /* Bus width */
+ uint8_t period; /* Sync rate factor */
+ uint8_t offset; /* Sync offset */
+ uint8_t ppr_options; /* Parallel Protocol Request options */
+};
+
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
+struct ahd_initiator_tinfo {
+ struct ahd_transinfo curr;
+ struct ahd_transinfo goal;
+ struct ahd_transinfo user;
+};
+
+/*
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping. For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
+ */
+struct ahd_tmode_tstate {
+ struct ahd_tmode_lstate* enabled_luns[AHD_NUM_LUNS];
+ struct ahd_initiator_tinfo transinfo[AHD_NUM_TARGETS];
+
+ /*
+ * Per initiator state bitmasks.
+ */
+ uint16_t auto_negotiate;/* Auto Negotiation Required */
+ uint16_t discenable; /* Disconnection allowed */
+ uint16_t tagenable; /* Tagged Queuing allowed */
+};
+
+/*
+ * Points of interest along the negotiated transfer scale.
+ */
+#define AHD_SYNCRATE_MAX 0x8
+#define AHD_SYNCRATE_160 0x8
+#define AHD_SYNCRATE_PACED 0x8
+#define AHD_SYNCRATE_DT 0x9
+#define AHD_SYNCRATE_ULTRA2 0xa
+#define AHD_SYNCRATE_ULTRA 0xc
+#define AHD_SYNCRATE_FAST 0x19
+#define AHD_SYNCRATE_MIN_DT AHD_SYNCRATE_FAST
+#define AHD_SYNCRATE_SYNC 0x32
+#define AHD_SYNCRATE_MIN 0x60
+#define AHD_SYNCRATE_ASYNC 0xFF
+
+/*
+ * In RevA, the synctable uses a 120MHz rate for the period
+ * factor 8 and 160MHz for the period factor 7. The 120MHz
+ * rate never made it into the official SCSI spec, so we must
+ * compensate when setting the negotiation table for Rev A
+ * parts.
+ */
+#define AHD_SYNCRATE_REVA_120 0x8
+#define AHD_SYNCRATE_REVA_160 0x7
+
+/***************************** Lookup Tables **********************************/
+/*
+ * Phase -> name and message out response
+ * to parity errors in each phase table.
+ */
+struct ahd_phase_table_entry {
+ uint8_t phase;
+ uint8_t mesg_out; /* Message response to parity errors */
+ char *phasemsg;
+};
+
+/************************** Serial EEPROM Format ******************************/
+
+struct seeprom_config {
+/*
+ * Per SCSI ID Configuration Flags
+ */
+ uint16_t device_flags[16]; /* words 0-15 */
+#define CFXFER 0x003F /* synchronous transfer rate */
+#define CFXFER_ASYNC 0x3F
+#define CFQAS 0x0040 /* Negotiate QAS */
+#define CFPACKETIZED 0x0080 /* Negotiate Packetized Transfers */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFDISC 0x0400 /* enable disconnection */
+#define CFMULTILUNDEV 0x0800 /* Probe multiple luns in BIOS scan */
+#define CFWIDEB 0x1000 /* wide bus device */
+#define CFHOSTMANAGED 0x8000 /* Managed by a RAID controller */
+
+/*
+ * BIOS Control Bits
+ */
+ uint16_t bios_control; /* word 16 */
+#define CFSUPREM 0x0001 /* support all removeable drives */
+#define CFSUPREMB 0x0002 /* support removeable boot drives */
+#define CFBIOSSTATE 0x000C /* BIOS Action State */
+#define CFBS_DISABLED 0x00
+#define CFBS_ENABLED 0x04
+#define CFBS_DISABLED_SCAN 0x08
+#define CFENABLEDV 0x0010 /* Perform Domain Validation */
+#define CFCTRL_A 0x0020 /* BIOS displays Ctrl-A message */
+#define CFSPARITY 0x0040 /* SCSI parity */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+#define CFBOOTCD 0x0100 /* Support Bootable CD-ROM */
+#define CFMSG_LEVEL 0x0600 /* BIOS Message Level */
+#define CFMSG_VERBOSE 0x0000
+#define CFMSG_SILENT 0x0200
+#define CFMSG_DIAG 0x0400
+#define CFRESETB 0x0800 /* reset SCSI bus at boot */
+/* UNUSED 0xf000 */
+
+/*
+ * Host Adapter Control Bits
+ */
+ uint16_t adapter_control; /* word 17 */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
+#define CFSTERM 0x0002 /* SCSI low byte termination */
+#define CFWSTERM 0x0004 /* SCSI high byte termination */
+#define CFSEAUTOTERM 0x0008 /* Ultra2 Perform secondary Auto Term*/
+#define CFSELOWTERM 0x0010 /* Ultra2 secondary low term */
+#define CFSEHIGHTERM 0x0020 /* Ultra2 secondary high term */
+#define CFSTPWLEVEL 0x0040 /* Termination level control */
+#define CFBIOSAUTOTERM 0x0080 /* Perform Auto termination */
+#define CFTERM_MENU 0x0100 /* BIOS displays termination menu */
+#define CFCLUSTERENB 0x8000 /* Cluster Enable */
+
+/*
+ * Bus Release Time, Host Adapter ID
+ */
+ uint16_t brtime_id; /* word 18 */
+#define CFSCSIID 0x000f /* host adapter SCSI ID */
+/* UNUSED 0x00f0 */
+#define CFBRTIME 0xff00 /* bus release time/PCI Latency Time */
+
+/*
+ * Maximum targets
+ */
+ uint16_t max_targets; /* word 19 */
+#define CFMAXTARG 0x00ff /* maximum targets */
+#define CFBOOTLUN 0x0f00 /* Lun to boot from */
+#define CFBOOTID 0xf000 /* Target to boot from */
+ uint16_t res_1[10]; /* words 20-29 */
+ uint16_t signature; /* BIOS Signature */
+#define CFSIGNATURE 0x400
+ uint16_t checksum; /* word 31 */
+};
+
+/****************************** Flexport Logic ********************************/
+#define FLXADDR_TERMCTL 0x0
+#define FLX_TERMCTL_ENSECHIGH 0x8
+#define FLX_TERMCTL_ENSECLOW 0x4
+#define FLX_TERMCTL_ENPRIHIGH 0x2
+#define FLX_TERMCTL_ENPRILOW 0x1
+#define FLXADDR_ROMSTAT_CURSENSECTL 0x1
+#define FLX_ROMSTAT_SEECFG 0xF0
+#define FLX_ROMSTAT_EECFG 0x0F
+#define FLX_ROMSTAT_SEE_93C66 0x00
+#define FLX_ROMSTAT_SEE_NONE 0xF0
+#define FLX_ROMSTAT_EE_512x8 0x0
+#define FLX_ROMSTAT_EE_1MBx8 0x1
+#define FLX_ROMSTAT_EE_2MBx8 0x2
+#define FLX_ROMSTAT_EE_4MBx8 0x3
+#define FLX_ROMSTAT_EE_16MBx8 0x4
+#define CURSENSE_ENB 0x1
+#define FLXADDR_FLEXSTAT 0x2
+#define FLX_FSTAT_BUSY 0x1
+#define FLXADDR_CURRENT_STAT 0x4
+#define FLX_CSTAT_SEC_HIGH 0xC0
+#define FLX_CSTAT_SEC_LOW 0x30
+#define FLX_CSTAT_PRI_HIGH 0x0C
+#define FLX_CSTAT_PRI_LOW 0x03
+#define FLX_CSTAT_MASK 0x03
+#define FLX_CSTAT_SHIFT 2
+#define FLX_CSTAT_OKAY 0x0
+#define FLX_CSTAT_OVER 0x1
+#define FLX_CSTAT_UNDER 0x2
+#define FLX_CSTAT_INVALID 0x3
+
+int ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+ u_int start_addr, u_int count);
+
+int ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+ u_int start_addr, u_int count);
+int ahd_wait_seeprom(struct ahd_softc *ahd);
+int ahd_verify_cksum(struct seeprom_config *sc);
+int ahd_acquire_seeprom(struct ahd_softc *ahd);
+void ahd_release_seeprom(struct ahd_softc *ahd);
+
+/**************************** Message Buffer *********************************/
+typedef enum {
+ MSG_FLAG_NONE = 0x00,
+ MSG_FLAG_EXPECT_PPR_BUSFREE = 0x01,
+ MSG_FLAG_IU_REQ_CHANGED = 0x02,
+ MSG_FLAG_EXPECT_IDE_BUSFREE = 0x04,
+ MSG_FLAG_PACKETIZED = 0x08
+} ahd_msg_flags;
+
+typedef enum {
+ MSG_TYPE_NONE = 0x00,
+ MSG_TYPE_INITIATOR_MSGOUT = 0x01,
+ MSG_TYPE_INITIATOR_MSGIN = 0x02,
+ MSG_TYPE_TARGET_MSGOUT = 0x03,
+ MSG_TYPE_TARGET_MSGIN = 0x04
+} ahd_msg_type;
+
+typedef enum {
+ MSGLOOP_IN_PROG,
+ MSGLOOP_MSGCOMPLETE,
+ MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+/*********************** Software Configuration Structure *********************/
+TAILQ_HEAD(scb_tailq, scb);
+
+struct ahd_suspend_channel_state {
+ uint8_t scsiseq;
+ uint8_t sxfrctl0;
+ uint8_t sxfrctl1;
+ uint8_t simode0;
+ uint8_t simode1;
+ uint8_t seltimer;
+ uint8_t seqctl;
+};
+
+struct ahd_suspend_state {
+ struct ahd_suspend_channel_state channel[2];
+ uint8_t optionmode;
+ uint8_t dscommand0;
+ uint8_t dspcistatus;
+ /* hsmailbox */
+ uint8_t crccontrol1;
+ uint8_t scbbaddr;
+ /* Host and sequencer SCB counts */
+ uint8_t dff_thrsh;
+ uint8_t *scratch_ram;
+ uint8_t *btt;
+};
+
+typedef void (*ahd_bus_intr_t)(struct ahd_softc *);
+
+typedef enum {
+ AHD_MODE_DFF0,
+ AHD_MODE_DFF1,
+ AHD_MODE_CCHAN,
+ AHD_MODE_SCSI,
+ AHD_MODE_CFG,
+ AHD_MODE_UNKNOWN
+} ahd_mode;
+
+#define AHD_MK_MSK(x) (0x01 << (x))
+#define AHD_MODE_DFF0_MSK AHD_MK_MSK(AHD_MODE_DFF0)
+#define AHD_MODE_DFF1_MSK AHD_MK_MSK(AHD_MODE_DFF1)
+#define AHD_MODE_CCHAN_MSK AHD_MK_MSK(AHD_MODE_CCHAN)
+#define AHD_MODE_SCSI_MSK AHD_MK_MSK(AHD_MODE_SCSI)
+#define AHD_MODE_CFG_MSK AHD_MK_MSK(AHD_MODE_CFG)
+#define AHD_MODE_UNKNOWN_MSK AHD_MK_MSK(AHD_MODE_UNKNOWN)
+#define AHD_MODE_ANY_MSK (~0)
+
+typedef uint8_t ahd_mode_state;
+
+typedef void ahd_callback_t (void *);
+
+struct ahd_softc {
+ bus_space_tag_t tags[2];
+ bus_space_handle_t bshs[2];
+#ifndef __linux__
+ bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */
+#endif
+ struct scb_data scb_data;
+
+ struct scb *next_queued_scb;
+
+ /*
+ * SCBs that have been sent to the controller
+ */
+ LIST_HEAD(, scb) pending_scbs;
+
+ /*
+ * Current register window mode information.
+ */
+ ahd_mode dst_mode;
+ ahd_mode src_mode;
+
+ /*
+ * Saved register window mode information
+ * used for restore on next unpause.
+ */
+ ahd_mode saved_dst_mode;
+ ahd_mode saved_src_mode;
+
+ /*
+ * Counting lock for deferring the release of additional
+ * untagged transactions from the untagged_queues. When
+ * the lock is decremented to 0, all queues in the
+ * untagged_queues array are run.
+ */
+ u_int untagged_queue_lock;
+
+ /*
+ * Per-target queue of untagged-transactions. The
+ * transaction at the head of the queue is the
+ * currently pending untagged transaction for the
+ * target. The driver only allows a single untagged
+ * transaction per target.
+ */
+ struct scb_tailq untagged_queues[AHD_NUM_TARGETS];
+
+ /*
+ * Platform specific data.
+ */
+ struct ahd_platform_data *platform_data;
+
+ /*
+ * Platform specific device information.
+ */
+ ahd_dev_softc_t dev_softc;
+
+ /*
+ * Bus specific device information.
+ */
+ ahd_bus_intr_t bus_intr;
+
+ /*
+ * Target mode related state kept on a per enabled lun basis.
+ * Targets that are not enabled will have null entries.
+ * As an initiator, we keep one target entry for our initiator
+ * ID to store our sync/wide transfer settings.
+ */
+ struct ahd_tmode_tstate *enabled_targets[AHD_NUM_TARGETS];
+
+ /*
+ * The black hole device responsible for handling requests for
+ * disabled luns on enabled targets.
+ */
+ struct ahd_tmode_lstate *black_hole;
+
+ /*
+ * Device instance currently on the bus awaiting a continue TIO
+ * for a command that was not given the disconnect priveledge.
+ */
+ struct ahd_tmode_lstate *pending_device;
+
+ /*
+ * Timer handles for timer driven callbacks.
+ */
+ ahd_timer_t reset_timer;
+
+ /*
+ * Card characteristics
+ */
+ ahd_chip chip;
+ ahd_feature features;
+ ahd_bug bugs;
+ ahd_flag flags;
+ struct seeprom_config *seep_config;
+
+ /* Values to store in the SEQCTL register for pause and unpause */
+ uint8_t unpause;
+ uint8_t pause;
+
+ /* Command Queues */
+ uint16_t qoutfifonext;
+ uint16_t qinfifonext;
+ uint16_t qinfifo[AHD_SCB_MAX];
+ uint16_t *qoutfifo;
+
+ /* Critical Section Data */
+ struct cs *critical_sections;
+ u_int num_critical_sections;
+
+ /* Buffer for handling packetized bitbucket. */
+ uint8_t *overrun_buf;
+
+ /* Links for chaining softcs */
+ TAILQ_ENTRY(ahd_softc) links;
+
+ /* Channel Names ('A', 'B', etc.) */
+ char channel;
+
+ /* Initiator Bus ID */
+ uint8_t our_id;
+
+ /*
+ * PCI error detection.
+ */
+ int unsolicited_ints;
+
+ /*
+ * Target incoming command FIFO.
+ */
+ struct target_cmd *targetcmds;
+ uint8_t tqinfifonext;
+
+ /*
+ * Incoming and outgoing message handling.
+ */
+ uint8_t send_msg_perror;
+ ahd_msg_flags msg_flags;
+ ahd_msg_type msg_type;
+ uint8_t msgout_buf[12];/* Message we are sending */
+ uint8_t msgin_buf[12];/* Message we are receiving */
+ u_int msgout_len; /* Length of message to send */
+ u_int msgout_index; /* Current index in msgout */
+ u_int msgin_index; /* Current index in msgin */
+
+ /*
+ * Mapping information for data structures shared
+ * between the sequencer and kernel.
+ */
+ bus_dma_tag_t parent_dmat;
+ bus_dma_tag_t shared_data_dmat;
+ bus_dmamap_t shared_data_dmamap;
+ bus_addr_t shared_data_busaddr;
+
+ /* Information saved through suspend/resume cycles */
+ struct ahd_suspend_state suspend_state;
+
+ /* Number of enabled target mode device on this card */
+ u_int enabled_luns;
+
+ /* Initialization level of this data structure */
+ u_int init_level;
+
+ /* PCI cacheline size. */
+ u_int pci_cachesize;
+
+ /* Per-Unit descriptive information */
+ const char *description;
+ const char *bus_description;
+ char *name;
+ int unit;
+
+ /* Selection Timer settings */
+ int seltime;
+
+ uint16_t user_discenable;/* Disconnection allowed */
+ uint16_t user_tagenable;/* Tagged Queuing allowed */
+};
+
+TAILQ_HEAD(ahd_softc_tailq, ahd_softc);
+extern struct ahd_softc_tailq ahd_tailq;
+
+/************************ Active Device Information ***************************/
+typedef enum {
+ ROLE_UNKNOWN,
+ ROLE_INITIATOR,
+ ROLE_TARGET
+} role_t;
+
+struct ahd_devinfo {
+ int our_scsiid;
+ int target_offset;
+ uint16_t target_mask;
+ u_int target;
+ u_int lun;
+ char channel;
+ role_t role; /*
+ * Only guaranteed to be correct if not
+ * in the busfree state.
+ */
+};
+
+/****************************** PCI Structures ********************************/
+#define AHD_PCI_IOADDR0 PCIR_MAPS /* I/O BAR*/
+#define AHD_PCI_MEMADDR (PCIR_MAPS + 4) /* Memory BAR */
+#define AHD_PCI_IOADDR1 (PCIR_MAPS + 12)/* Second I/O BAR */
+
+typedef int (ahd_device_setup_t)(struct ahd_softc *);
+
+struct ahd_pci_identity {
+ uint64_t full_id;
+ uint64_t id_mask;
+ char *name;
+ ahd_device_setup_t *setup;
+};
+extern struct ahd_pci_identity ahd_pci_ident_table [];
+extern const u_int ahd_num_pci_devs;
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+ uint32_t full_id;
+ uint32_t id_mask;
+ char *name;
+ ahd_device_setup_t *setup;
+};
+extern struct aic7770_identity aic7770_ident_table [];
+extern const int ahd_num_aic7770_devs;
+
+#define AHD_EISA_SLOT_OFFSET 0xc00
+#define AHD_EISA_IOSIZE 0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+u_int ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl);
+void ahd_set_disconnected_list(struct ahd_softc *ahd,
+ u_int target, u_int lun,
+ u_int scbid);
+void ahd_busy_tcl(struct ahd_softc *ahd,
+ u_int tcl, u_int busyid);
+static __inline void ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl);
+static __inline void
+ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl)
+{
+ ahd_busy_tcl(ahd, tcl, SCB_LIST_NULL);
+}
+
+/***************************** PCI Front End *********************************/
+struct ahd_pci_identity *ahd_find_pci_device(ahd_dev_softc_t);
+int ahd_pci_config(struct ahd_softc *,
+ struct ahd_pci_identity *);
+
+/*************************** EISA/VL Front End ********************************/
+struct aic7770_identity *aic7770_find_device(uint32_t);
+int aic7770_config(struct ahd_softc *ahd,
+ struct aic7770_identity *);
+
+/************************** SCB and SCB queue management **********************/
+int ahd_probe_scbs(struct ahd_softc *);
+void ahd_run_untagged_queues(struct ahd_softc *ahd);
+void ahd_run_untagged_queue(struct ahd_softc *ahd,
+ struct scb_tailq *queue);
+void ahd_qinfifo_requeue_tail(struct ahd_softc *ahd,
+ struct scb *scb);
+int ahd_match_scb(struct ahd_softc *ahd, struct scb *scb,
+ int target, char channel, int lun,
+ u_int tag, role_t role);
+
+/****************************** Initialization ********************************/
+struct ahd_softc *ahd_alloc(void *platform_arg, char *name);
+int ahd_softc_init(struct ahd_softc *);
+void ahd_controller_info(struct ahd_softc *ahd, char *buf);
+int ahd_init(struct ahd_softc *ahd);
+int ahd_default_config(struct ahd_softc *ahd);
+int ahd_parse_cfgdata(struct ahd_softc *ahd,
+ struct seeprom_config *sc);
+void ahd_intr_enable(struct ahd_softc *ahd, int enable);
+void ahd_pause_and_flushwork(struct ahd_softc *ahd);
+int ahd_suspend(struct ahd_softc *ahd);
+int ahd_resume(struct ahd_softc *ahd);
+void ahd_softc_insert(struct ahd_softc *);
+struct ahd_softc *ahd_find_softc(struct ahd_softc *ahd);
+void ahd_set_unit(struct ahd_softc *, int);
+void ahd_set_name(struct ahd_softc *, char *);
+void ahd_alloc_scbs(struct ahd_softc *ahd);
+void ahd_free(struct ahd_softc *ahd);
+int ahd_reset(struct ahd_softc *ahd);
+void ahd_shutdown(void *arg);
+int ahd_write_flexport(struct ahd_softc *ahd,
+ u_int addr, u_int value);
+int ahd_read_flexport(struct ahd_softc *ahd, u_int addr,
+ uint8_t *value);
+int ahd_wait_flexport(struct ahd_softc *ahd);
+
+/*************************** Interrupt Services *******************************/
+void ahd_pci_intr(struct ahd_softc *ahd);
+void ahd_clear_intstat(struct ahd_softc *ahd);
+void ahd_run_qoutfifo(struct ahd_softc *ahd);
+#ifdef AHD_TARGET_MODE
+void ahd_run_tqinfifo(struct ahd_softc *ahd, int paused);
+#endif
+void ahd_handle_hwerrint(struct ahd_softc *ahd);
+void ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat);
+void ahd_handle_scsiint(struct ahd_softc *ahd,
+ u_int intstat);
+void ahd_clear_critical_section(struct ahd_softc *ahd);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+ SEARCH_COMPLETE,
+ SEARCH_COUNT,
+ SEARCH_REMOVE,
+ SEARCH_PRINT
+} ahd_search_action;
+int ahd_search_qinfifo(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status,
+ ahd_search_action action);
+int ahd_search_disc_list(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ int stop_on_first, int remove,
+ int save_state);
+void ahd_freeze_devq(struct ahd_softc *ahd, struct scb *scb);
+int ahd_reset_channel(struct ahd_softc *ahd, char channel,
+ int initiate_reset);
+int ahd_abort_scbs(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+void ahd_restart(struct ahd_softc *ahd);
+void ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo);
+void ahd_handle_scb_status(struct ahd_softc *ahd,
+ struct scb *scb);
+void ahd_handle_scsi_status(struct ahd_softc *ahd,
+ struct scb *scb);
+void ahd_calc_residual(struct ahd_softc *ahd,
+ struct scb *scb);
+/*************************** Utility Functions ********************************/
+struct ahd_phase_table_entry*
+ ahd_lookup_phase_entry(int phase);
+void ahd_compile_devinfo(struct ahd_devinfo *devinfo,
+ u_int our_id, u_int target,
+ u_int lun, char channel,
+ role_t role);
+/************************** Transfer Negotiation ******************************/
+void ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
+ u_int *ppr_options, u_int maxsync);
+void ahd_validate_offset(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *tinfo,
+ u_int period, u_int *offset,
+ int wide, role_t role);
+void ahd_validate_width(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *tinfo,
+ u_int *bus_width,
+ role_t role);
+int ahd_update_neg_request(struct ahd_softc*,
+ struct ahd_devinfo*,
+ struct ahd_tmode_tstate*,
+ struct ahd_initiator_tinfo*,
+ int /*force*/);
+void ahd_set_width(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int width, u_int type, int paused);
+void ahd_set_syncrate(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int period, u_int offset,
+ u_int ppr_options,
+ u_int type, int paused);
+typedef enum {
+ AHD_QUEUE_NONE,
+ AHD_QUEUE_BASIC,
+ AHD_QUEUE_TAGGED
+} ahd_queue_alg;
+
+void ahd_set_tags(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ ahd_queue_alg alg);
+
+/**************************** Target Mode *************************************/
+#ifdef AHD_TARGET_MODE
+void ahd_send_lstate_events(struct ahd_softc *,
+ struct ahd_tmode_lstate *);
+void ahd_handle_en_lun(struct ahd_softc *ahd,
+ struct cam_sim *sim, union ccb *ccb);
+cam_status ahd_find_tmode_devs(struct ahd_softc *ahd,
+ struct cam_sim *sim, union ccb *ccb,
+ struct ahd_tmode_tstate **tstate,
+ struct ahd_tmode_lstate **lstate,
+ int notfound_failure);
+#ifndef AHD_TMODE_ENABLE
+#define AHD_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+#ifdef AHD_DEBUG
+extern uint32_t ahd_debug;
+#define AHD_SHOW_MISC 0x001
+#define AHD_SHOW_SENSE 0x002
+#define AHD_DUMP_SEEPROM 0x004
+#define AHD_SHOW_TERMCTL 0x008
+#define AHD_SHOW_MEMORY 0x010
+#define AHD_SHOW_MESSAGES 0x020
+#define AHD_SHOW_MODEPTR 0x040
+#define AHD_SHOW_SELTO 0x080
+#define AHD_SHOW_FIFOS 0x100
+#define AHD_SHOW_QFULL 0x200
+#define AHD_SHOW_QUEUE 0x400
+#define AHD_SHOW_TQIN 0x800
+#endif
+void ahd_print_scb(struct scb *scb);
+void ahd_dump_sglist(struct scb *scb);
+void ahd_dump_all_cards_state(void);
+void ahd_dump_card_state(struct ahd_softc *ahd);
+void ahd_dump_scbs(struct ahd_softc *ahd);
+#endif /* _AIC79XX_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx.reg b/xen/drivers/scsi/aic7xxx/aic79xx.reg
new file mode 100644
index 0000000000..b681bd3ea3
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx.reg
@@ -0,0 +1,3716 @@
+/*
+ * Aic79xx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#34 $"
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic79xx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic79xx driver.
+ */
+
+/* Register window Modes */
+#define M_DFF0 0
+#define M_DFF1 1
+#define M_CCHAN 2
+#define M_SCSI 3
+#define M_CFG 4
+#define M_DST_SHIFT 4
+
+#define MK_MODE(src, dst) ((src) | ((dst) << M_DST_SHIFT))
+#define SET_MODE(src, dst) \
+ SET_SRC_MODE src; \
+ SET_DST_MODE dst; \
+ mvi MK_MODE(src, dst) call set_mode_work_around
+
+/*
+ * Mode Pointer
+ * Controls which of the 5, 512byte, address spaces should be used
+ * as the source and destination of any register accesses in our
+ * register window.
+ */
+register MODE_PTR {
+ address 0x000
+ access_mode RW
+ mask DST_MODE 0x70
+ mask SRC_MODE 0x07
+ mode_pointer
+}
+
+const SRC_MODE_SHIFT 0
+const DST_MODE_SHIFT 4
+
+/*
+ * Host Interrupt Status
+ */
+register INTSTAT {
+ address 0x001
+ access_mode RW
+ bit HWERRINT 0x80
+ bit BRKADRINT 0x40
+ bit SWTMINT 0x20
+ bit PCIINT 0x10
+ bit SCSIINT 0x08
+ bit SEQINT 0x04
+ bit CMDCMPLT 0x02
+ bit SPLTINT 0x01
+ mask INT_PEND 0xFF
+}
+
+/*
+ * Sequencer Interrupt Code
+ */
+register SEQINTCODE {
+ address 0x002
+ access_mode RW
+ mask BAD_PHASE 1 /* unknown scsi bus phase */
+ mask SEND_REJECT 2 /* sending a message reject */
+ mask PROTO_VIOLATION 3 /* Protocol Violation */
+ mask NO_MATCH 4 /* no cmd match for reconnect */
+ mask IGN_WIDE_RES 5 /* Complex IGN Wide Res Msg */
+ mask PDATA_REINIT 6 /*
+ * Returned to data phase
+ * that requires data
+ * transfer pointers to be
+ * recalculated from the
+ * transfer residual.
+ */
+ mask HOST_MSG_LOOP 7 /*
+ * The bus is ready for the
+ * host to perform another
+ * message transaction. This
+ * mechanism is used for things
+ * like sync/wide negotiation
+ * that require a kernel based
+ * message state engine.
+ */
+ mask BAD_STATUS 8 /* Bad status from target */
+ mask DATA_OVERRUN 9 /*
+ * Target attempted to write
+ * beyond the bounds of its
+ * command.
+ */
+ mask MKMSG_FAILED 10 /*
+ * Target completed command
+ * without honoring our ATN
+ * request to issue a message.
+ */
+ mask MISSED_BUSFREE 11 /*
+ * The sequencer never saw
+ * the bus go free after
+ * either a command complete
+ * or disconnect message.
+ */
+ mask SCB_MISMATCH 12 /*
+ * Downloaded SCB's tag does
+ * not match the entry we
+ * intended to download.
+ */
+ mask NO_FREE_SCB 13 /*
+ * get_free_or_disc_scb failed.
+ */
+ mask OUT_OF_RANGE 14
+ mask NO_FREE_FIFO 15
+ mask DUMP_CARD_STATE 16
+ mask ILLEGAL_PHASE 17
+ mask INVALID_SEQINT 18
+ mask CFG4ISTAT_INTR 19
+ mask STATUS_OVERRUN 20
+ mask CFG4OVERRUN 21
+ mask SNAPSHOTCLRCHN 22
+ mask MONITORDRAIN 23
+ mask ENTERING_NONPACK 24
+ mask PCIX_ARBITOR_WW 25
+}
+
+/*
+ * Clear Host Interrupt
+ */
+register CLRINT {
+ address 0x003
+ access_mode WO
+ bit CLRBRKADRINT 0x40
+ bit CLRSWTMINT 0x20
+ bit CLRSCSIINT 0x08
+ bit CLRSEQINT 0x04
+ bit CLRCMDINT 0x02
+ bit CLRSPLTINT 0x01
+}
+
+/*
+ * Error Register
+ */
+register ERROR {
+ address 0x004
+ access_mode RO
+ bit CIOPARERR 0x80
+ bit MPARERR 0x20
+ bit DPARERR 0x10
+ bit SQPARERR 0x08
+ bit ILLOPCODE 0x04
+ bit DSCTMOUT 0x02
+}
+
+/*
+ * Clear Error
+ */
+register CLRERR {
+ address 0x004
+ access_mode WO
+ bit CLRCIOPARERR 0x80
+ bit CLRMPARERR 0x20
+ bit CLRDPARERR 0x10
+ bit CLRSQPARERR 0x08
+ bit CLRILLOPCODE 0x04
+ bit CLRDSCTMOUT 0x02
+}
+
+/*
+ * Host Control Register
+ * Overall host control of the device.
+ */
+register HCNTRL {
+ address 0x005
+ access_mode RW
+ bit POWRDN 0x40
+ bit SWINT 0x10
+ bit HCNTRL3 0x08
+ bit PAUSE 0x04
+ bit INTEN 0x02
+ bit CHIPRST 0x01
+ bit CHIPRSTACK 0x01
+}
+
+/*
+ * Host New SCB Queue Offset
+ */
+register HNSCB_QOFF {
+ address 0x006
+ access_mode RW
+ size 2
+}
+
+/*
+ * Host Empty SCB Queue Offset
+ */
+register HESCB_QOFF {
+ address 0x008
+ access_mode RW
+}
+
+/*
+ * Host Mailbox
+ */
+register HS_MAILBOX {
+ address 0x0B
+ access_mode RW
+ mask HOST_TQINPOS 0x80 /* Boundary at either 0 or 128 */
+}
+
+/*
+ * Sequencer Interupt Status
+ */
+register SEQINTSTAT {
+ address 0x0C
+ access_mode RO
+ bit SEQ_SWTMRTO 0x10
+ bit SEQ_SEQINT 0x08
+ bit SEQ_SCSIINT 0x04
+ bit SEQ_PCIINT 0x02
+ bit SEQ_SPLTINT 0x01
+}
+
+/*
+ * Clear SEQ Interrupt
+ */
+register CLRSEQINTSTAT {
+ address 0x0C0
+ access_mode WO
+ bit CLRSEQ_SWTMRTO 0x10
+ bit CLRSEQ_SEQINT 0x08
+ bit CLRSEQ_SCSIINT 0x04
+ bit CLRSEQ_PCIINT 0x02
+ bit CLRSEQ_SPLTINT 0x01
+}
+
+/*
+ * Software Timer
+ */
+register SWTIMER {
+ address 0x0E0
+ access_mode RW
+ size 2
+}
+
+/*
+ * SEQ New SCB Queue Offset
+ */
+register SNSCB_QOFF {
+ address 0x010
+ access_mode RW
+ size 2
+ modes M_CCHAN
+}
+
+/*
+ * SEQ Empty SCB Queue Offset
+ */
+register SESCB_QOFF {
+ address 0x012
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * SEQ Done SCB Queue Offset
+ */
+register SDSCB_QOFF {
+ address 0x014
+ access_mode RW
+ modes M_CCHAN
+ size 2
+}
+
+/*
+ * Queue Offset Control & Status
+ */
+register QOFF_CTLSTA {
+ address 0x016
+ access_mode RW
+ modes M_CCHAN
+ bit EMPTY_SCB_AVAIL 0x80
+ bit NEW_SCB_AVAIL 0x40
+ bit SDSCB_ROLLOVR 0x20
+ bit HS_MAILBOX_ACT 0x10
+ mask SCB_QSIZE 0x0F
+ mask SCB_QSIZE_4 0x00
+ mask SCB_QSIZE_8 0x01
+ mask SCB_QSIZE_16 0x02
+ mask SCB_QSIZE_32 0x03
+ mask SCB_QSIZE_64 0x04
+ mask SCB_QSIZE_128 0x05
+ mask SCB_QSIZE_256 0x06
+ mask SCB_QSIZE_512 0x07
+ mask SCB_QSIZE_1024 0x08
+ mask SCB_QSIZE_2048 0x09
+ mask SCB_QSIZE_4096 0x0A
+ mask SCB_QSIZE_8192 0x0B
+ mask SCB_QSIZE_16384 0x0C
+}
+
+/*
+ * Interrupt Control
+ */
+register INTCTL {
+ address 0x018
+ access_mode RW
+ bit SWTMINTMASK 0x80
+ bit SWTMINTEN 0x40
+ bit SWTIMER_START 0x20
+ bit AUTOCLRCMDINT 0x10
+ bit PCIINTEN 0x08
+ bit SCSIINTEN 0x04
+ bit SEQINTEN 0x02
+ bit SPLTINTEN 0x01
+}
+
+/*
+ * Data FIFO Control
+ */
+register DFCNTRL {
+ address 0x019
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ bit PRELOADEN 0x80
+ bit SCSIEN 0x20
+ bit SCSIENACK 0x20
+ bit HDMAEN 0x08
+ bit HDMAENACK 0x08
+ bit DIRECTION 0x04
+ bit DIRECTIONACK 0x04
+ bit FIFOFLUSH 0x02
+ bit FIFOFLUSHACK 0x02
+ bit DIRECTIONEN 0x01
+}
+
+/*
+ * Device Space Command 0
+ */
+register DSCOMMAND0 {
+ address 0x019
+ access_mode RW
+ modes M_CFG
+ bit CACHETHEN 0x80 /* Cache Threshold enable */
+ bit DPARCKEN 0x40 /* Data Parity Check Enable */
+ bit MPARCKEN 0x20 /* Memory Parity Check Enable */
+ bit EXTREQLCK 0x10 /* External Request Lock */
+ bit CIOPARCKEN 0x01 /* Internal bus parity error enable */
+}
+
+/*
+ * Data FIFO Status
+ */
+register DFSTATUS {
+ address 0x01A
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ bit PRELOAD_AVAIL 0x80
+ bit PKT_PRELOAD_AVAIL 0x40
+ bit MREQPEND 0x10
+ bit HDONE 0x08
+ bit DFTHRESH 0x04
+ bit FIFOFULL 0x02
+ bit FIFOEMP 0x01
+}
+
+/*
+ * S/G Cache Pointer
+ */
+register SG_CACHE_PRE {
+ address 0x01B
+ access_mode WO
+ modes M_DFF0, M_DFF1
+ mask SG_ADDR_MASK 0xf8
+ bit ODD_SEG 0x04
+ bit LAST_SEG 0x02
+}
+
+register SG_CACHE_SHADOW {
+ address 0x01B
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ mask SG_ADDR_MASK 0xf8
+ bit ODD_SEG 0x04
+ bit LAST_SEG 0x02
+ bit LAST_SEG_DONE 0x01
+}
+
+/*
+ * Arbiter Control
+ */
+register ARBCTL {
+ address 0x01B
+ access_mode RW
+ modes M_CFG
+ bit RESET_HARB 0x80
+ bit RETRY_SWEN 0x08
+ mask USE_TIME 0x07
+}
+
+/*
+ * Data Channel Host Address
+ */
+register HADDR {
+ address 0x070
+ access_mode RW
+ size 8
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Host Overlay DMA Address
+ */
+register HODMAADR {
+ address 0x070
+ access_mode RW
+ size 8
+ modes M_SCSI
+}
+
+/*
+ * Data Channel Host Count
+ */
+register HCNT {
+ address 0x078
+ access_mode RW
+ size 3
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Host Overlay DMA Count
+ */
+register HODMACNT {
+ address 0x078
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Host Overlay DMA Enable
+ */
+register HODMAEN {
+ address 0x07A
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Scatter/Gather Host Address
+ */
+register SGHADDR {
+ address 0x07C
+ access_mode RW
+ size 8
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * SCB Host Address
+ */
+register SCBHADDR {
+ address 0x07C
+ access_mode RW
+ size 8
+ modes M_CCHAN
+}
+
+/*
+ * Scatter/Gather Host Count
+ */
+register SGHCNT {
+ address 0x084
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * SCB Host Count
+ */
+register SCBHCNT {
+ address 0x084
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * Data FIFO Threshold
+ */
+register DFF_THRSH {
+ address 0x088
+ access_mode RW
+ modes M_CFG
+ mask WR_DFTHRSH 0x70
+ mask RD_DFTHRSH 0x07
+ mask RD_DFTHRSH_MIN 0x00
+ mask RD_DFTHRSH_25 0x01
+ mask RD_DFTHRSH_50 0x02
+ mask RD_DFTHRSH_63 0x03
+ mask RD_DFTHRSH_75 0x04
+ mask RD_DFTHRSH_85 0x05
+ mask RD_DFTHRSH_90 0x06
+ mask RD_DFTHRSH_MAX 0x07
+ mask WR_DFTHRSH_MIN 0x00
+ mask WR_DFTHRSH_25 0x10
+ mask WR_DFTHRSH_50 0x20
+ mask WR_DFTHRSH_63 0x30
+ mask WR_DFTHRSH_75 0x40
+ mask WR_DFTHRSH_85 0x50
+ mask WR_DFTHRSH_90 0x60
+ mask WR_DFTHRSH_MAX 0x70
+}
+
+/*
+ * ROM Address
+ */
+register ROMADDR {
+ address 0x08A
+ access_mode RW
+ size 3
+}
+
+/*
+ * ROM Control
+ */
+register ROMCNTRL {
+ address 0x08D
+ access_mode RW
+ mask ROMOP 0xE0
+ mask ROMSPD 0x18
+ bit REPEAT 0x02
+ bit RDY 0x01
+}
+
+/*
+ * ROM Data
+ */
+register ROMDATA {
+ address 0x08E
+ access_mode RW
+}
+
+/*
+ * Data Channel Receive Message 0
+ */
+register DCHRXMSG0 {
+ address 0x090
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ mask CDNUM 0xF8
+ mask CFNUM 0x07
+}
+
+/*
+ * CMC Recieve Message 0
+ */
+register CMCRXMSG0 {
+ address 0x090
+ access_mode RO
+ modes M_CCHAN
+ mask CDNUM 0xF8
+ mask CFNUM 0x07
+}
+
+/*
+ * Overlay Recieve Message 0
+ */
+register OVLYRXMSG0 {
+ address 0x090
+ access_mode RO
+ modes M_SCSI
+ mask CDNUM 0xF8
+ mask CFNUM 0x07
+}
+
+/*
+ * Relaxed Order Enable
+ */
+register ROENABLE {
+ address 0x090
+ access_mode RW
+ modes M_CFG
+ bit MSIROEN 0x20
+ bit OVLYROEN 0x10
+ bit CMCROEN 0x08
+ bit SGROEN 0x04
+ bit DCH1ROEN 0x02
+ bit DCH0ROEN 0x01
+}
+
+/*
+ * Data Channel Receive Message 1
+ */
+register DCHRXMSG1 {
+ address 0x091
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ mask CBNUM 0xFF
+}
+
+/*
+ * CMC Recieve Message 1
+ */
+register CMCRXMSG1 {
+ address 0x091
+ access_mode RO
+ modes M_CCHAN
+ mask CBNUM 0xFF
+}
+
+/*
+ * Overlay Recieve Message 1
+ */
+register OVLYRXMSG1 {
+ address 0x091
+ access_mode RO
+ modes M_SCSI
+ mask CBNUM 0xFF
+}
+
+/*
+ * No Snoop Enable
+ */
+register NSENABLE {
+ address 0x091
+ access_mode RW
+ modes M_CFG
+ bit MSINSEN 0x20
+ bit OVLYNSEN 0x10
+ bit CMCNSEN 0x08
+ bit SGNSEN 0x04
+ bit DCH1NSEN 0x02
+ bit DCH0NSEN 0x01
+}
+
+/*
+ * Data Channel Receive Message 2
+ */
+register DCHRXMSG2 {
+ address 0x092
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ mask MINDEX 0xFF
+}
+
+/*
+ * CMC Recieve Message 2
+ */
+register CMCRXMSG2 {
+ address 0x092
+ access_mode RO
+ modes M_CCHAN
+ mask MINDEX 0xFF
+}
+
+/*
+ * Overlay Recieve Message 2
+ */
+register OVLYRXMSG2 {
+ address 0x092
+ access_mode RO
+ modes M_SCSI
+ mask MINDEX 0xFF
+}
+
+/*
+ * Outstanding Split Transactions
+ */
+register OST {
+ address 0x092
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Data Channel Receive Message 3
+ */
+register DCHRXMSG3 {
+ address 0x093
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ mask MCLASS 0x0F
+}
+
+/*
+ * CMC Recieve Message 3
+ */
+register CMCRXMSG3 {
+ address 0x093
+ access_mode RO
+ modes M_CCHAN
+ mask MCLASS 0x0F
+}
+
+/*
+ * Overlay Recieve Message 3
+ */
+register OVLYRXMSG3 {
+ address 0x093
+ access_mode RO
+ modes M_SCSI
+ mask MCLASS 0x0F
+}
+
+/*
+ * PCI-X Control
+ */
+register PCIXCTL {
+ address 0x093
+ access_mode RW
+ modes M_CFG
+ bit SERRPULSE 0x80
+ bit UNEXPSCIEN 0x20
+ bit SPLTSMADIS 0x10
+ bit SPLTSTADIS 0x08
+ bit SRSPDPEEN 0x04
+ bit TSCSERREN 0x02
+ bit CMPABCDIS 0x01
+}
+
+/*
+ * CMC Sequencer Byte Count
+ */
+register CMCSEQBCNT {
+ address 0x094
+ access_mode RO
+ modes M_CCHAN
+}
+
+/*
+ * Overlay Sequencer Byte Count
+ */
+register OVLYSEQBCNT {
+ address 0x094
+ access_mode RO
+ modes M_SCSI
+}
+
+/*
+ * Data Channel Sequencer Byte Count
+ */
+register DCHSEQBCNT {
+ address 0x094
+ access_mode RO
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Data Channel Split Status 0
+ */
+register DCHSPLTSTAT0 {
+ address 0x096
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ bit STAETERM 0x80
+ bit SCBCERR 0x40
+ bit SCADERR 0x20
+ bit SCDATBUCKET 0x10
+ bit CNTNOTCMPLT 0x08
+ bit RXOVRUN 0x04
+ bit RXSCEMSG 0x02
+ bit RXSPLTRSP 0x01
+}
+
+/*
+ * CMC Split Status 0
+ */
+register CMCSPLTSTAT0 {
+ address 0x096
+ access_mode RW
+ modes M_CCHAN
+ bit STAETERM 0x80
+ bit SCBCERR 0x40
+ bit SCADERR 0x20
+ bit SCDATBUCKET 0x10
+ bit CNTNOTCMPLT 0x08
+ bit RXOVRUN 0x04
+ bit RXSCEMSG 0x02
+ bit RXSPLTRSP 0x01
+}
+
+/*
+ * Overlay Split Status 0
+ */
+register OVLYSPLTSTAT0 {
+ address 0x096
+ access_mode RW
+ modes M_SCSI
+ bit STAETERM 0x80
+ bit SCBCERR 0x40
+ bit SCADERR 0x20
+ bit SCDATBUCKET 0x10
+ bit CNTNOTCMPLT 0x08
+ bit RXOVRUN 0x04
+ bit RXSCEMSG 0x02
+ bit RXSPLTRSP 0x01
+}
+
+/*
+ * Data Channel Split Status 1
+ */
+register DCHSPLTSTAT1 {
+ address 0x097
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ bit RXDATABUCKET 0x01
+}
+
+/*
+ * CMC Split Status 1
+ */
+register CMCSPLTSTAT1 {
+ address 0x097
+ access_mode RW
+ modes M_CCHAN
+ bit RXDATABUCKET 0x01
+}
+
+/*
+ * Overlay Split Status 1
+ */
+register OVLYSPLTSTAT1 {
+ address 0x097
+ access_mode RW
+ modes M_SCSI
+ bit RXDATABUCKET 0x01
+}
+
+/*
+ * S/G Receive Message 0
+ */
+register SGRXMSG0 {
+ address 0x098
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ mask CDNUM 0xF8
+ mask CFNUM 0x07
+}
+
+/*
+ * S/G Receive Message 1
+ */
+register SGRXMSG1 {
+ address 0x099
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ mask CBNUM 0xFF
+}
+
+/*
+ * S/G Receive Message 2
+ */
+register SGRXMSG2 {
+ address 0x09A
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ mask MINDEX 0xFF
+}
+
+/*
+ * S/G Receive Message 3
+ */
+register SGRXMSG3 {
+ address 0x09B
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ mask MCLASS 0x0F
+}
+
+/*
+ * Slave Split Out Address 0
+ */
+register SLVSPLTOUTADR0 {
+ address 0x098
+ access_mode RO
+ modes M_SCSI
+ mask LOWER_ADDR 0x7F
+}
+
+/*
+ * Slave Split Out Address 1
+ */
+register SLVSPLTOUTADR1 {
+ address 0x099
+ access_mode RO
+ modes M_SCSI
+ mask REQ_DNUM 0xF8
+ mask REQ_FNUM 0x07
+}
+
+/*
+ * Slave Split Out Address 2
+ */
+register SLVSPLTOUTADR2 {
+ address 0x09A
+ access_mode RO
+ modes M_SCSI
+ mask REQ_BNUM 0xFF
+}
+
+/*
+ * Slave Split Out Address 3
+ */
+register SLVSPLTOUTADR3 {
+ address 0x09B
+ access_mode RO
+ modes M_SCSI
+ bit RLXORD 020
+ mask TAG_NUM 0x1F
+}
+
+/*
+ * SG Sequencer Byte Count
+ */
+register SGSEQBCNT {
+ address 0x09C
+ access_mode RO
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Slave Split Out Attribute 0
+ */
+register SLVSPLTOUTATTR0 {
+ address 0x09C
+ access_mode RO
+ modes M_SCSI
+ mask LOWER_BCNT 0xFF
+}
+
+/*
+ * Slave Split Out Attribute 1
+ */
+register SLVSPLTOUTATTR1 {
+ address 0x09D
+ access_mode RO
+ modes M_SCSI
+ mask CMPLT_DNUM 0xF8
+ mask CMPLT_FNUM 0x07
+}
+
+/*
+ * Slave Split Out Attribute 2
+ */
+register SLVSPLTOUTATTR2 {
+ address 0x09E
+ access_mode RO
+ size 2
+ modes M_SCSI
+ mask CMPLT_BNUM 0xFF
+}
+/*
+ * S/G Split Status 0
+ */
+register SGSPLTSTAT0 {
+ address 0x09E
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ bit STAETERM 0x80
+ bit SCBCERR 0x40
+ bit SCADERR 0x20
+ bit SCDATBUCKET 0x10
+ bit CNTNOTCMPLT 0x08
+ bit RXOVRUN 0x04
+ bit RXSCEMSG 0x02
+ bit RXSPLTRSP 0x01
+}
+
+/*
+ * S/G Split Status 1
+ */
+register SGSPLTSTAT1 {
+ address 0x09F
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ bit RXDATABUCKET 0x01
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+ address 0x09f
+ access_mode RW
+ modes M_CFG
+ mask TEST_GROUP 0xF0
+ mask TEST_NUM 0x0F
+}
+
+/*
+ * Data FIFO 0 PCI Status
+ */
+register DF0PCISTAT {
+ address 0x0A0
+ access_mode RW
+ modes M_CFG
+ bit DPE 0x80
+ bit SSE 0x40
+ bit RMA 0x20
+ bit RTA 0x10
+ bit SCAAPERR 0x08
+ bit RDPERR 0x04
+ bit TWATERR 0x02
+ bit DPR 0x01
+}
+
+/*
+ * Data FIFO 1 PCI Status
+ */
+register DF1PCISTAT {
+ address 0x0A1
+ access_mode RW
+ modes M_CFG
+ bit DPE 0x80
+ bit SSE 0x40
+ bit RMA 0x20
+ bit RTA 0x10
+ bit SCAAPERR 0x08
+ bit RDPERR 0x04
+ bit TWATERR 0x02
+ bit DPR 0x01
+}
+
+/*
+ * S/G PCI Status
+ */
+register SGPCISTAT {
+ address 0x0A2
+ access_mode RW
+ modes M_CFG
+ bit DPE 0x80
+ bit SSE 0x40
+ bit RMA 0x20
+ bit RTA 0x10
+ bit SCAAPERR 0x08
+ bit RDPERR 0x04
+ bit DPR 0x01
+}
+
+/*
+ * CMC PCI Status
+ */
+register CMCPCISTAT {
+ address 0x0A3
+ access_mode RW
+ modes M_CFG
+ bit DPE 0x80
+ bit SSE 0x40
+ bit RMA 0x20
+ bit RTA 0x10
+ bit SCAAPERR 0x08
+ bit RDPERR 0x04
+ bit TWATERR 0x02
+ bit DPR 0x01
+}
+
+/*
+ * Overlay PCI Status
+ */
+register OVLYPCISTAT {
+ address 0x0A4
+ access_mode RW
+ modes M_CFG
+ bit DPE 0x80
+ bit SSE 0x40
+ bit RMA 0x20
+ bit RTA 0x10
+ bit SCAAPERR 0x08
+ bit RDPERR 0x04
+ bit DPR 0x01
+}
+
+/*
+ * PCI Status for MSI Master DMA Transfer
+ */
+register MSIPCISTAT {
+ address 0x0A6
+ access_mode RW
+ modes M_CFG
+ bit SSE 0x40
+ bit RMA 0x20
+ bit RTA 0x10
+ bit CLRPENDMSI 0x08
+ bit TWATERR 0x02
+ bit DPR 0x01
+}
+
+/*
+ * PCI Status for Target
+ */
+register TARGPCISTAT {
+ address 0x0A6
+ access_mode RW
+ modes M_CFG
+ bit DPE 0x80
+ bit SSE 0x40
+ bit STA 0x08
+ bit TWATERR 0x02
+}
+
+/*
+ * LQ Packet In
+ * The last LQ Packet recieved
+ */
+register LQIN {
+ address 0x020
+ access_mode RW
+ size 20
+ modes M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * SCB Type Pointer
+ * SCB offset for Target Mode SCB type information
+ */
+register TYPEPTR {
+ address 0x020
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Queue Tag Pointer
+ * SCB offset to the Two Byte tag identifier used for target mode.
+ */
+register TAGPTR {
+ address 0x021
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Logical Unit Number Pointer
+ * SCB offset to the LSB (little endian) of the lun field.
+ */
+register LUNPTR {
+ address 0x022
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Data Length Pointer
+ * SCB offset for the 4 byte data length field in target mode.
+ */
+register DATALENPTR {
+ address 0x023
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Status Length Pointer
+ * SCB offset to the two byte status field in target SCBs.
+ */
+register STATLENPTR {
+ address 0x024
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Command Length Pointer
+ * Scb offset for the CDB length field in initiator SCBs.
+ */
+register CMDLENPTR {
+ address 0x025
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Task Attribute Pointer
+ * Scb offset for the byte field specifying the attribute byte
+ * to be used in command packets.
+ */
+register ATTRPTR {
+ address 0x026
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Task Management Flags Pointer
+ * Scb offset for the byte field specifying the attribute flags
+ * byte to be used in command packets.
+ */
+register FLAGPTR {
+ address 0x027
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Command Pointer
+ * Scb offset for the first byte in the CDB for initiator SCBs.
+ */
+register CMDPTR {
+ address 0x028
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Queue Next Pointer
+ * Scb offset for the 2 byte "next scb link".
+ */
+register QNEXTPTR {
+ address 0x029
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * SCSI ID Pointer
+ * Scb offset to the value to place in the SCSIID register
+ * during target mode connections.
+ */
+register IDPTR {
+ address 0x02A
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Command Aborted Byte Pointer
+ * Offset to the SCB flags field that includes the
+ * "SCB aborted" status bit.
+ */
+register ABRTBYTEPTR {
+ address 0x02B
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Command Aborted Bit Pointer
+ * Bit offset in the SCB flags field for "SCB aborted" status.
+ */
+register ABRTBITPTR {
+ address 0x02C
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Logical Unit Number Length
+ * The length, in bytes, of the SCB lun field.
+ */
+register LUNLEN {
+ address 0x030
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * CDB Limit
+ * The size, in bytes, of the embedded CDB field in initator SCBs.
+ */
+register CDBLIMIT {
+ address 0x031
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Maximum Commands
+ * The maximum number of commands to issue during a
+ * single packetized connection.
+ */
+register MAXCMD {
+ address 0x032
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Maximum Command Counter
+ * The number of commands already sent during this connection
+ */
+register MAXCMDCNT {
+ address 0x033
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * LQ Packet Reserved Bytes
+ * The bytes to be sent in the currently reserved fileds
+ * of all LQ packets.
+ */
+register LQRSVD01 {
+ address 0x034
+ access_mode RW
+ modes M_SCSI
+}
+register LQRSVD16 {
+ address 0x035
+ access_mode RW
+ modes M_SCSI
+}
+register LQRSVD17 {
+ address 0x036
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Command Reserved 0
+ * The byte to be sent for the reserved byte 0 of
+ * outgoing command packets.
+ */
+register CMDRSVD0 {
+ address 0x037
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * LQ Manager Control 0
+ */
+register LQCTL0 {
+ address 0x038
+ access_mode RW
+ modes M_CFG
+ mask LQITARGCLT 0xC0
+ mask LQIINITGCLT 0x30
+ mask LQ0TARGCLT 0x0C
+ mask LQ0INITGCLT 0x03
+}
+
+/*
+ * LQ Manager Control 1
+ */
+register LQCTL1 {
+ address 0x038
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit PCI2PCI 0x04
+ bit SINGLECMD 0x02
+ bit ABORTPENDING 0x01
+}
+
+/*
+ * LQ Manager Control 2
+ */
+register LQCTL2 {
+ address 0x039
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit LQIRETRY 0x80
+ bit LQICONTINUE 0x40
+ bit LQITOIDLE 0x20
+ bit LQIPAUSE 0x10
+ bit LQORETRY 0x08
+ bit LQOCONTINUE 0x04
+ bit LQOTOIDLE 0x02
+ bit LQOPAUSE 0x01
+}
+
+/*
+ * SCSI RAM BIST0
+ */
+register SCSBIST0 {
+ address 0x039
+ access_mode RW
+ modes M_CFG
+ bit GSBISTERR 0x40
+ bit GSBISTDONE 0x20
+ bit GSBISTRUN 0x10
+ bit OSBISTERR 0x04
+ bit OSBISTDONE 0x02
+ bit OSBISTRUN 0x01
+}
+
+/*
+ * SCSI Sequence Control0
+ */
+register SCSISEQ0 {
+ address 0x03A
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit TEMODEO 0x80
+ bit ENSELO 0x40
+ bit ENARBO 0x20
+ bit FORCEBUSFREE 0x10
+ bit SCSIRSTO 0x01
+}
+
+/*
+ * SCSI RAM BIST 1
+ */
+register SCSBIST1 {
+ address 0x03A
+ access_mode RW
+ modes M_CFG
+ bit NTBISTERR 0x04
+ bit NTBISTDONE 0x02
+ bit NTBISTRUN 0x01
+}
+
+/*
+ * SCSI Sequence Control 1
+ */
+register SCSISEQ1 {
+ address 0x03B
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit MANUALCTL 0x40
+ bit ENSELI 0x20
+ bit ENRSELI 0x10
+ mask MANUALP 0x0C
+ bit ENAUTOATNP 0x02
+ bit ALTSTIM 0x01
+}
+
+/*
+ * SCSI Transfer Control 0
+ */
+register SXFRCTL0 {
+ address 0x03C
+ access_mode RW
+ modes M_SCSI
+ bit DFON 0x80
+ bit DFPEXP 0x40
+ bit BIOSCANCELEN 0x10
+ bit SPIOEN 0x08
+}
+
+/*
+ * SCSI Transfer Control 1
+ */
+register SXFRCTL1 {
+ address 0x03D
+ access_mode RW
+ modes M_SCSI
+ bit BITBUCKET 0x80
+ bit ENSACHK 0x40
+ bit ENSPCHK 0x20
+ mask STIMESEL 0x18
+ bit ENSTIMER 0x04
+ bit ACTNEGEN 0x02
+ bit STPWEN 0x01
+}
+
+/*
+ * SCSI Transfer Control 2
+ */
+register SXFRCTL2 {
+ address 0x03E
+ access_mode RW
+ modes M_SCSI
+ bit AUTORSTDIS 0x10
+ bit CMDDMAEN 0x08
+ mask ASU 0x07
+}
+
+/*
+ * SCSI Bus Initiator IDs
+ * Bitmask of observed initiators on the bus.
+ */
+register BUSINITID {
+ address 0x03C
+ access_mode RW
+ modes M_CFG
+ size 2
+}
+
+/*
+ * Data Length Counters
+ * Packet byte counter.
+ */
+register DLCOUNT {
+ address 0x03C
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ size 3
+}
+
+/*
+ * Data FIFO Status
+ */
+register DFFSTAT {
+ address 0x03F
+ access_mode RW
+ modes M_SCSI
+ bit FIFO1FREE 0x20
+ bit FIFO0FREE 0x10
+ bit CURRFIFO 0x01
+}
+
+/*
+ * SCSI Bus Target IDs
+ * Bitmask of observed targets on the bus.
+ */
+register BUSTARGID {
+ address 0x03E
+ access_mode RW
+ modes M_CFG
+ size 2
+}
+
+/*
+ * SCSI Control Signal Out
+ */
+register SCSISIGO {
+ address 0x040
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CDO 0x80
+ bit IOO 0x40
+ bit MSGO 0x20
+ bit ATNO 0x10
+ bit SELO 0x08
+ bit BSYO 0x04
+ bit REQO 0x02
+ bit ACKO 0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+ mask PHASE_MASK CDO|IOO|MSGO
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOO
+ mask P_DATAOUT_DT P_DATAOUT|MSGO
+ mask P_DATAIN_DT P_DATAIN|MSGO
+ mask P_COMMAND CDO
+ mask P_MESGOUT CDO|MSGO
+ mask P_STATUS CDO|IOO
+ mask P_MESGIN CDO|IOO|MSGO
+}
+
+register SCSISIGI {
+ address 0x041
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CDI 0x80
+ bit IOI 0x40
+ bit MSGI 0x20
+ bit ATNI 0x10
+ bit SELI 0x08
+ bit BSYI 0x04
+ bit REQI 0x02
+ bit ACKI 0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_DATAOUT_DT P_DATAOUT|MSGI
+ mask P_DATAIN_DT P_DATAIN|MSGI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+}
+
+/*
+ * Multiple Target IDs
+ * Bitmask of ids to respond as a target.
+ */
+register MULTARGID {
+ address 0x040
+ access_mode RW
+ modes M_CFG
+ size 2
+}
+
+/*
+ * SCSI Phase
+ */
+register SCSIPHASE {
+ address 0x042
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit STATUS_PHASE 0x20
+ bit COMMAND_PHASE 0x10
+ bit MSG_IN_PHASE 0x08
+ bit MSG_OUT_PHASE 0x04
+ bit DATA_IN_PHASE 0x02
+ bit DATA_OUT_PHASE 0x01
+ mask DATA_PHASE_MASK 0x03
+}
+
+/*
+ * SCSI Data 0 Image
+ */
+register SCSIDAT0_IMG {
+ address 0x043
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * SCSI Latched Data
+ */
+register SCSIDAT {
+ address 0x044
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ size 2
+}
+
+/*
+ * SCSI Data Bus
+ */
+register SCSIBUS {
+ address 0x046
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ size 2
+}
+
+/*
+ * Target ID In
+ */
+register TARGIDIN {
+ address 0x048
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CLKOUT 0x80
+ mask TARGID 0x0F
+}
+
+/*
+ * Selection/Reselection ID
+ * Upper four bits are the device id. The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+ address 0x049
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ mask SELID_MASK 0xf0
+ bit ONEBIT 0x08
+}
+
+/*
+ * SCSI Block Control
+ * Controls Bus type and channel selection. SELWIDE allows for the
+ * coexistence of 8bit and 16bit devices on a wide bus.
+ */
+register SBLKCTL {
+ address 0x04A
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit DIAGLEDEN 0x80
+ bit DIAGLEDON 0x40
+ bit ENAB40 0x08 /* LVD transceiver active */
+ bit ENAB20 0x04 /* SE/HVD transceiver active */
+ bit SELWIDE 0x02
+}
+
+/*
+ * Option Mode
+ */
+register OPTIONMODE {
+ address 0x04A
+ access_mode RW
+ modes M_CFG
+ bit BIOSCANCTL 0x80
+ bit AUTOACKEN 0x40
+ bit BIASCANCTL 0x20
+ bit BUSFREEREV 0x10
+ bit ENDGFORMCHK 0x04
+ bit AUTO_MSGOUT_DE 0x02
+ mask OPTIONMODE_DEFAULTS AUTO_MSGOUT_DE
+}
+
+/*
+ * SCSI Status 0
+ */
+register SSTAT0 {
+ address 0x04B
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit TARGET 0x80 /* Board acting as target */
+ bit SELDO 0x40 /* Selection Done */
+ bit SELDI 0x20 /* Board has been selected */
+ bit SELINGO 0x10 /* Selection In Progress */
+ bit IOERR 0x08 /* LVD Tranceiver mode changed */
+ bit OVERRUN 0x04 /* SCSI Offset overrun detected */
+ bit SPIORDY 0x02 /* SCSI PIO Ready */
+ bit ARBDO 0x01 /* Arbitration Done Out */
+}
+
+/*
+ * Clear SCSI Interrupt 0
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+ address 0x04B
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CLRSELDO 0x40
+ bit CLRSELDI 0x20
+ bit CLRSELINGO 0x10
+ bit CLRIOERR 0x08
+ bit CLROVERRUN 0x04
+ bit CLRSPIORDY 0x02
+ bit CLRARBDO 0x01
+}
+
+/*
+ * SCSI Interrupt Mode 0
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+ address 0x04B
+ access_mode RW
+ modes M_CFG
+ bit ENSELDO 0x40
+ bit ENSELDI 0x20
+ bit ENSELINGO 0x10
+ bit ENIOERR 0x08
+ bit ENOVERRUN 0x04
+ bit ENSPIORDY 0x02
+ bit ENARBDO 0x01
+}
+
+/*
+ * SCSI Status 1
+ */
+register SSTAT1 {
+ address 0x04C
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit SELTO 0x80
+ bit ATNTARG 0x40
+ bit SCSIRSTI 0x20
+ bit PHASEMIS 0x10
+ bit BUSFREE 0x08
+ bit SCSIPERR 0x04
+ bit STRB2FAST 0x02
+ bit REQINIT 0x01
+}
+
+/*
+ * Clear SCSI Interrupt 1
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+ address 0x04c
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CLRSELTIMEO 0x80
+ bit CLRATNO 0x40
+ bit CLRSCSIRSTI 0x20
+ bit CLRBUSFREE 0x08
+ bit CLRSCSIPERR 0x04
+ bit CLRSTRB2FAST 0x02
+ bit CLRREQINIT 0x01
+}
+
+/*
+ * SCSI Status 2
+ */
+register SSTAT2 {
+ address 0x04d
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ mask BUSFREETIME 0xc0
+ mask BUSFREE_LQO 0x40
+ mask BUSFREE_DFF0 0x80
+ mask BUSFREE_DFF1 0xC0
+ bit NONPACKREQ 0x20
+ bit EXP_ACTIVE 0x10 /* SCSI Expander Active */
+ bit BSYX 0x08 /* Busy Expander */
+ bit WIDE_RES 0x04 /* Modes 0 and 1 only */
+ bit SDONE 0x02 /* Modes 0 and 1 only */
+ bit DMADONE 0x01 /* Modes 0 and 1 only */
+}
+
+/*
+ * Clear SCSI Interrupt 2
+ */
+register CLRSINT2 {
+ address 0x04D
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CLRNONPACKREQ 0x20
+ bit CLRWIDE_RES 0x04 /* Modes 0 and 1 only */
+ bit CLRSDONE 0x02 /* Modes 0 and 1 only */
+ bit CLRDMADONE 0x01 /* Modes 0 and 1 only */
+}
+
+/*
+ * SCSI Interrupt Mode 2
+ */
+register SIMODE2 {
+ address 0x04D
+ access_mode RW
+ modes M_CFG
+ bit ENWIDE_RES 0x04
+ bit ENSDONE 0x02
+ bit ENDMADONE 0x01
+}
+
+/*
+ * Physical Error Diagnosis
+ */
+register PERRDIAG {
+ address 0x04E
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit HIZERO 0x80
+ bit HIPERR 0x40
+ bit PREVPHASE 0x20
+ bit PARITYERR 0x10
+ bit AIPERR 0x08
+ bit CRCERR 0x04
+ bit DGFORMERR 0x02
+ bit DTERR 0x01
+}
+
+/*
+ * LQI Manager Current State
+ */
+register LQISTATE {
+ address 0x04E
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * SCSI Offset Count
+ */
+register SOFFCNT {
+ address 0x04F
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * LQO Manager Current State
+ */
+register LQOSTATE {
+ address 0x04F
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * LQI Manager Status
+ */
+register LQISTAT0 {
+ address 0x050
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit LQIATNQAS 0x20
+ bit LQICRCT1 0x10
+ bit LQICRCT2 0x08
+ bit LQIBADLQT 0x04
+ bit LQIATNLQ 0x02
+ bit LQIATNCMD 0x01
+}
+
+/*
+ * Clear LQI Interrupts 0
+ */
+register CLRLQIINTO {
+ address 0x050
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CLRLQIATNQAS 0x20
+ bit CLRLQICRCT1 0x10
+ bit CLRLQICRCT2 0x08
+ bit CLRLQIBADLQT 0x04
+ bit CLRLQIATNLQ 0x02
+ bit CLRLQIATNCMD 0x01
+}
+
+/*
+ * LQI Manager Interrupt Mode 0
+ */
+register LQIMODE0 {
+ address 0x050
+ access_mode RW
+ modes M_CFG
+ bit ENLQIATNQASK 0x20
+ bit ENLQICRCT1 0x10
+ bit ENLQICRCT2 0x08
+ bit ENLQIBADLQT 0x04
+ bit ENLQIATNLQ 0x02
+ bit ENLQIATNCMD 0x01
+}
+
+/*
+ * LQI Manager Status 1
+ */
+register LQISTAT1 {
+ address 0x051
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ mask LQIPHASE_LQ 0x80
+ mask LQIPHASE_NLQ 0x40
+ bit LQIABORT 0x20
+ mask LQICRCI_LQ 0x10
+ mask LQICRCI_NLQ 0x08
+ bit LQIBADLQI 0x04
+ mask LQIOVERI_LQ 0x02
+ mask LQIOVERI_NLQ 0x01
+}
+
+/*
+ * Clear LQI Manager Interrupts1
+ */
+register CLRLQIINT1 {
+ address 0x051
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ mask CLRLQIPHASE_LQ 0x80
+ mask CLRLQIPHASE_NLQ 0x40
+ bit CLRLIQABORT 0x20
+ mask CLRLQICRCI_LQ 0x10
+ mask CLRLQICRCI_NLQ 0x08
+ bit CLRLQIBADLQI 0x04
+ mask CLRLQIOVERI_LQ 0x02
+ mask CLRLQIOVERI_NLQ 0x01
+}
+
+/*
+ * LQI Manager Interrupt Mode 1
+ */
+register LQIMODE1 {
+ address 0x051
+ access_mode RW
+ modes M_CFG
+ mask ENLQIPHASE_LQ 0x80
+ mask ENLQIPHASE_NLQ 0x40
+ bit ENLIQABORT 0x20
+ mask ENLQICRCI_LQ 0x10
+ mask ENLQICRCI_NLQ 0x08
+ bit ENLQIBADLQI 0x04
+ mask ENLQIOVERI_LQ 0x02
+ mask ENLQIOVERI_NLQ 0x01
+}
+
+/*
+ * LQI Manager Status 2
+ */
+register LQISTAT2 {
+ address 0x052
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit PACKETIZED 0x80
+ bit LQIPHASE_OUTPKT 0x40
+ bit LQIWORKONLQ 0x20
+ bit LQIWAITFIFO 0x10
+ bit LQISTOPPKT 0x08
+ bit LQISTOPLQ 0x04
+ bit LQISTOPCMD 0x02
+ bit LQIGSAVAIL 0x01
+}
+
+/*
+ * SCSI Status 3
+ */
+register SSTAT3 {
+ address 0x053
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit NTRAMPERR 0x02
+ bit OSRAMPERR 0x01
+}
+
+/*
+ * Clear SCSI Status 3
+ */
+register CLRSINT3 {
+ address 0x053
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CLRNTRAMPERR 0x02
+ bit CLROSRAMPERR 0x01
+}
+
+/*
+ * SCSI Interrupt Mode 3
+ */
+register SIMODE3 {
+ address 0x053
+ access_mode RW
+ modes M_CFG
+ bit ENNTRAMPERR 0x02
+ bit ENOSRAMPERR 0x01
+}
+
+/*
+ * LQO Manager Status 0
+ */
+register LQOSTAT0 {
+ address 0x054
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit LQOTARGSCBPERR 0x10
+ bit LQOSTOPT2 0x08
+ bit LQOATNLQ 0x04
+ bit LQOATNPKT 0x02
+ bit LQOTCRC 0x01
+}
+
+/*
+ * Clear LQO Manager interrupt 0
+ */
+register CLRLQOINT0 {
+ address 0x054
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CLRLQOTARGSCBPERR 0x10
+ bit CLRLQOSTOPT2 0x08
+ bit CLRLQOATNLQ 0x04
+ bit CLRLQOATNPKT 0x02
+ bit CLRLQOTCRC 0x01
+}
+
+/*
+ * LQO Manager Interrupt Mode 0
+ */
+register LQOMODE0 {
+ address 0x054
+ access_mode RW
+ modes M_CFG
+ bit ENLQOTARGSCBPERR 0x10
+ bit ENLQOSTOPT2 0x08
+ bit ENLQOATNLQ 0x04
+ bit ENLQOATNPKT 0x02
+ bit ENLQOTCRC 0x01
+}
+
+/*
+ * LQO Manager Status 1
+ */
+register LQOSTAT1 {
+ address 0x055
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit LQOINITSCBPERR 0x10
+ bit LQOSTOPI2 0x08
+ bit LQOBADQAS 0x04
+ bit LQOBUSFREE 0x02
+ bit LQOPHACHGINPKT 0x01
+}
+
+/*
+ * Clear LOQ Interrupt 1
+ */
+register CLRLQOINT1 {
+ address 0x055
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit CLRLQOINITSCBPERR 0x10
+ bit CLRLQOSTOPI2 0x08
+ bit CLRLQOBADQAS 0x04
+ bit CLRLQOBUSFREE 0x02
+ bit CLRLQOPHACHGINPKT 0x01
+}
+
+/*
+ * LQO Manager Interrupt Mode 1
+ */
+register LQOMODE1 {
+ address 0x055
+ access_mode RW
+ modes M_CFG
+ bit ENLQOINITSCBPERR 0x10
+ bit ENLQOSTOPI2 0x08
+ bit ENLQOBADQAS 0x04
+ bit ENLQOBUSFREE 0x02
+ bit ENLQOPHACHGINPKT 0x01
+}
+
+/*
+ * LQO Manager Status 2
+ */
+register LQOSTAT2 {
+ address 0x056
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ mask LQOPKT 0xE0
+ bit LQOWAITFIFO 0x10
+ bit LQOPHACHGOUTPKT 0x02 /* outside of packet boundaries. */
+ bit LQOSTOP0 0x01 /* Stopped after sending all packets */
+}
+
+/*
+ * Output Synchronizer Space Count
+ */
+register OS_SPACE_CNT {
+ address 0x056
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * SCSI Interrupt Mode 1
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+ address 0x057
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ bit ENSELTIMO 0x80
+ bit ENATNTARG 0x40
+ bit ENSCSIRST 0x20
+ bit ENPHASEMIS 0x10
+ bit ENBUSFREE 0x08
+ bit ENSCSIPERR 0x04
+ bit ENSTRB2FAST 0x02
+ bit ENREQINIT 0x01
+}
+
+/*
+ * Good Status FIFO
+ */
+register GSFIFO {
+ address 0x058
+ access_mode RO
+ size 2
+ modes M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * Data FIFO SCSI Transfer Control
+ */
+register DFFSXFRCTL {
+ address 0x05A
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ bit CLRSHCNT 0x04
+ bit CLRCHN 0x02
+ bit RSTCHN 0x01
+}
+
+/*
+ * Next SCSI Control Block
+ */
+register NEXTSCB {
+ address 0x05A
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * SEQ Interrupts
+ */
+register SEQINTSRC {
+ address 0x05B
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ bit CTXTDONE 0x40
+ bit SAVEPTRS 0x20
+ bit CFG4DATA 0x10
+ bit CFG4ISTAT 0x08
+ bit CFG4TSTAT 0x04
+ bit CFG4ICMD 0x02
+ bit CFG4TCMD 0x01
+}
+
+/*
+ * Clear Arp Interrupts
+ */
+register CLRSEQINTSRC {
+ address 0x05B
+ access_mode WO
+ modes M_DFF0, M_DFF1
+ bit CLRCTXTDONE 0x40
+ bit CLRSAVEPTRS 0x20
+ bit CLRCFG4DATA 0x10
+ bit CLRCFG4ISTAT 0x08
+ bit CLRCFG4TSTAT 0x04
+ bit CLRCFG4ICMD 0x02
+ bit CLRCFG4TCMD 0x01
+}
+
+/*
+ * SEQ Interrupt Enabled (Shared)
+ */
+register SEQIMODE {
+ address 0x05C
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ bit ENCTXTDONE 0x40
+ bit ENSAVEPTRS 0x20
+ bit ENCFG4DATA 0x10
+ bit ENCFG4ISTAT 0x08
+ bit ENCFG4TSTAT 0x04
+ bit ENCFG4ICMD 0x02
+ bit ENCFG4TCMD 0x01
+}
+
+/*
+ * Current SCSI Control Block
+ */
+register CURRSCB {
+ address 0x05C
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Data FIFO Status
+ */
+register MDFFSTAT {
+ address 0x05D
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ bit LASTSDONE 0x10
+ bit SHVALID 0x08
+ bit DLZERO 0x04 /* FIFO data ends on packet boundary. */
+ bit DATAINFIFO 0x02
+ bit FIFOFREE 0x01
+}
+
+/*
+ * CRC Control
+ */
+register CRCCONTROL {
+ address 0x05d
+ access_mode RW
+ modes M_CFG
+ bit CRCVALCHKEN 0x40
+}
+
+/*
+ * SCSI Test Control
+ */
+register SCSITEST {
+ address 0x05E
+ access_mode RW
+ modes M_CFG
+ bit CNTRTEST 0x08
+ bit SEL_TXPLL_DEBUG 0x04
+}
+
+/*
+ * Data FIFO Queue Tag
+ */
+register DFFTAG {
+ address 0x05E
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Last SCSI Control Block
+ */
+register LASTSCB {
+ address 0x05E
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * SCSI I/O Cell Power-down Control
+ */
+register IOPDNCTL {
+ address 0x05F
+ access_mode RW
+ modes M_CFG
+ bit DISABLE_OE 0x80
+ bit PDN_IDIST 0x04
+ bit PDN_DIFFSENSE 0x01
+}
+
+/*
+ * Shaddow Host Address.
+ */
+register SHADDR {
+ address 0x060
+ access_mode RO
+ size 8
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Data Group CRC Interval.
+ */
+register DGRPCRCI {
+ address 0x060
+ access_mode RW
+ size 2
+ modes M_CFG
+}
+
+/*
+ * Data Transfer Negotiation Address
+ */
+register NEGOADDR {
+ address 0x060
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Data Transfer Negotiation Data - Period Byte
+ */
+register NEGPERIOD {
+ address 0x061
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Packetized CRC Interval
+ */
+register PACKCRCI {
+ address 0x062
+ access_mode RW
+ size 2
+ modes M_CFG
+}
+
+/*
+ * Data Transfer Negotiation Data - Offset Byte
+ */
+register NEGOFFSET {
+ address 0x062
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Data Transfer Negotiation Data - PPR Options
+ */
+register NEGPPROPTS {
+ address 0x063
+ access_mode RW
+ modes M_SCSI
+ bit PPROPT_PACE 0x08
+ bit PPROPT_QAS 0x04
+ bit PPROPT_DT 0x02
+ bit PPROPT_IUT 0x01
+}
+
+/*
+ * Data Transfer Negotiation Data - Connection Options
+ */
+register NEGCONOPTS {
+ address 0x064
+ access_mode RW
+ modes M_SCSI
+ bit ENAIP 0x08
+ bit ENAUTOATNI 0x04
+ bit ENAUTOATNO 0x02
+ bit WIDEXFER 0x01
+}
+
+/*
+ * Negotiation Table Annex Column Index.
+ */
+register ANNEXCOL {
+ address 0x065
+ access_mode RW
+ modes M_SCSI
+}
+
+const AHD_ANNEXCOL_PRECOMP 4
+const AHD_PRECOMP_MASK 0x07
+const AHD_PRECOMP_CUTBACK_17 0x04
+const AHD_PRECOMP_CUTBACK_29 0x06
+const AHD_PRECOMP_CUTBACK_37 0x07
+const AHD_PRECOMP_FASTSLEW 0x40
+const AHD_NUM_ANNEXCOLS 4
+
+/*
+ * Negotiation Table Annex Data Port.
+ */
+register ANNEXDAT {
+ address 0x066
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Initiator's Own Id.
+ * The SCSI ID to use for Selection Out and seen during a reselection..
+ */
+register IOWNID {
+ address 0x067
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * 960MHz Phase-Locked Loop Control 0
+ */
+register PLL960CTL0 {
+ address 0x068
+ access_mode RW
+ modes M_CFG
+ bit PLL_VCOSEL 0x80
+ bit PLL_PWDN 0x40
+ mask PLL_NS 0x30
+ bit PLL_ENLUD 0x08
+ bit PLL_ENLPF 0x04
+ bit PLL_DLPF 0x02
+ bit PLL_ENFBM 0x01
+}
+
+/*
+ * Target Own Id
+ */
+register TOWNID {
+ address 0x069
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * 960MHz Phase-Locked Loop Control 1
+ */
+register PLL960CTL1 {
+ address 0x069
+ access_mode RW
+ modes M_CFG
+ bit PLL_CNTEN 0x80
+ bit PLL_CNTCLR 0x40
+ bit PLL_RST 0x01
+}
+
+/*
+ * Expander Signature
+ */
+register XSIG {
+ address 0x06A
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Shadow Byte Count
+ */
+register SHCNT {
+ address 0x068
+ access_mode RW
+ size 3
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Selection Out ID
+ */
+register SELOID {
+ address 0x06B
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * 960-MHz Phase-Locked Loop Test Count
+ */
+register PLL960CNT0 {
+ address 0x06A
+ access_mode RO
+ size 2
+ modes M_CFG
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Control 0
+ */
+register PLL400CTL0 {
+ address 0x06C
+ access_mode RW
+ modes M_CFG
+ bit PLL_VCOSEL 0x80
+ bit PLL_PWDN 0x40
+ mask PLL_NS 0x30
+ bit PLL_ENLUD 0x08
+ bit PLL_ENLPF 0x04
+ bit PLL_DLPF 0x02
+ bit PLL_ENFBM 0x01
+}
+
+/*
+ * Arbitration Fairness
+ */
+register FAIRNESS {
+ address 0x06C
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Control 1
+ */
+register PLL400CTL1 {
+ address 0x06D
+ access_mode RW
+ modes M_CFG
+ bit PLL_CNTEN 0x80
+ bit PLL_CNTCLR 0x40
+ bit PLL_RST 0x01
+}
+
+/*
+ * Arbitration Unfairness
+ */
+register UNFAIRNESS {
+ address 0x06E
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Test Count
+ */
+register PLL400CNT0 {
+ address 0x06E
+ access_mode RO
+ size 2
+ modes M_CFG
+}
+
+/*
+ * SCB Page Pointer
+ */
+register SCBPTR {
+ address 0x0A8
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1, M_CCHAN, M_SCSI
+}
+
+/*
+ * CMC SCB Array Count
+ * Number of bytes to transfer between CMC SCB memory and SCBRAM.
+ * Transfers must be 8byte aligned and sized.
+ */
+register CCSCBACNT {
+ address 0x0AB
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * SCB Autopointer
+ * SCB-Next Address Snooping logic. When an SCB is transferred to
+ * the card, the next SCB address to be used by the CMC array can
+ * be autoloaded from that transfer.
+ */
+register SCBAUTOPTR {
+ address 0x0AB
+ access_mode RW
+ modes M_CFG
+ bit AUSCBPTR_EN 0x80
+ mask SCBPTR_ADDR 0x38
+ mask SCBPTR_OFF 0x07
+}
+
+/*
+ * CMC SG Ram Address Pointer
+ */
+register CCSGADDR {
+ address 0x0AC
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * CMC SCB RAM Address Pointer
+ */
+register CCSCBADDR {
+ address 0x0AC
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * CMC SCB Ram Back-up Address Pointer
+ * Indicates the true stop location of transfers halted prior
+ * to SCBHCNT going to 0.
+ */
+register CCSCBADR_BK {
+ address 0x0AC
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * CMC SG Control
+ */
+register CCSGCTL {
+ address 0x0AD
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ bit CCSGDONE 0x80
+ bit SG_CACHE_AVAIL 0x10
+ bit CCSGEN 0x08
+ bit SG_FETCH_REQ 0x02
+ bit CCSGRESET 0x01
+}
+
+/*
+ * CMD SCB Control
+ */
+register CCSCBCTL {
+ address 0x0AD
+ access_mode RW
+ modes M_CCHAN
+ bit CCSCBDONE 0x80
+ bit ARRDONE 0x40
+ bit CCARREN 0x10
+ bit CCSCBEN 0x08
+ bit CCSCBDIR 0x04
+ bit CCSCBRESET 0x01
+}
+
+/*
+ * CMC Ram BIST
+ */
+register CMC_RAMBIST {
+ address 0x0AD
+ access_mode RW
+ modes M_CFG
+ bit SG_ELEMENT_SIZE 0x80
+ bit SCBRAMBIST_FAIL 0x40
+ bit SG_BIST_FAIL 0x20
+ bit SG_BIST_EN 0x10
+ bit CMC_BUFFER_BIST_FAIL 0x02
+ bit CMC_BUFFER_BIST_EN 0x01
+}
+
+/*
+ * CMC SG RAM Data Port
+ */
+register CCSGRAM {
+ address 0x0B0
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * CMC SCB RAM Data Port
+ */
+register CCSCBRAM {
+ address 0x0B0
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * Flex DMA Address.
+ */
+register FLEXADR {
+ address 0x0B0
+ access_mode RW
+ size 3
+ modes M_SCSI
+}
+
+/*
+ * Flex DMA Byte Count
+ */
+register FLEXCNT {
+ address 0x0B3
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Flex DMA Status
+ */
+register FLEXDMASTAT {
+ address 0x0B5
+ access_mode RW
+ modes M_SCSI
+ bit FLEXDMAERR 0x02
+ bit FLEXDMADONE 0x01
+}
+
+/*
+ * Flex DMA Data Port
+ */
+register FLEXDATA {
+ address 0x0B6
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Board Data
+ */
+register BRDDAT {
+ address 0x0B8
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Board Control
+ */
+register BRDCTL {
+ address 0x0B9
+ access_mode RW
+ modes M_SCSI
+ bit FLXARBACK 0x80
+ bit FLXARBREQ 0x40
+ mask BRDADDR 0x38
+ bit BRDEN 0x04
+ bit BRDRW 0x02
+ bit BRDSTB 0x01
+}
+
+/*
+ * Serial EEPROM Address
+ */
+register SEEADR {
+ address 0x0BA
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Serial EEPROM Data
+ */
+register SEEDAT {
+ address 0x0BC
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Serial EEPROM Status
+ */
+register SEESTAT {
+ address 0x0BE
+ access_mode RO
+ modes M_SCSI
+ bit INIT_DONE 0x80
+ mask SEEOPCODE 0x70
+ bit LDALTID_L 0x08
+ bit SEEARBACK 0x04
+ bit SEEBUSY 0x02
+ bit SEESTART 0x01
+}
+
+/*
+ * Serial EEPROM Control
+ */
+register SEECTL {
+ address 0x0BE
+ access_mode RW
+ modes M_SCSI
+ mask SEEOPCODE 0x70
+ mask SEEOP_ERASE 0x70
+ mask SEEOP_READ 0x60
+ mask SEEOP_WRITE 0x50
+ /*
+ * The following four commands use special
+ * addresses for differentiation.
+ */
+ mask SEEOP_ERAL 0x40
+ mask SEEOP_EWEN 0x40
+ mask SEEOP_WALL 0x40
+ mask SEEOP_EWDS 0x40
+ bit SEERST 0x02
+ bit SEESTART 0x01
+}
+
+const SEEOP_ERAL_ADDR 0x80
+const SEEOP_EWEN_ADDR 0xC0
+const SEEOP_WRAL_ADDR 0x40
+const SEEOP_EWDS_ADDR 0x00
+
+/*
+ * SCB Counter
+ */
+register SCBCNT {
+ address 0x0BF
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Data FIFO Write Address
+ * Pointer to the next QWD location to be written to the data FIFO.
+ */
+register DFWADDR {
+ address 0x0C0
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * DSP Filter Control
+ */
+register DSPFLTRCTL {
+ address 0x0C0
+ access_mode RW
+ modes M_CFG
+ bit FLTRDISABLE 0x20
+ bit EDGESENSE 0x10
+ mask DSPFCNTSEL 0x0F
+}
+
+/*
+ * DSP Data Channel Control
+ */
+register DSPDATACTL {
+ address 0x0C1
+ access_mode RW
+ modes M_CFG
+ bit BYPASSENAB 0x80
+ bit DESQDIS 0x10
+ bit RCVROFFSTDIS 0x04
+ bit XMITOFFSTDIS 0x02
+}
+
+/*
+ * Data FIFO Read Address
+ * Pointer to the next QWD location to be read from the data FIFO.
+ */
+register DFRADDR {
+ address 0x0C2
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * DSP REQ Control
+ */
+register DSPREQCTL {
+ address 0x0C2
+ access_mode RW
+ modes M_CFG
+ mask MANREQCTL 0xC0
+ mask MANREQDLY 0x3F
+}
+
+/*
+ * DSP ACK Control
+ */
+register DSPACKCTL {
+ address 0x0C3
+ access_mode RW
+ modes M_CFG
+ mask MANACKCTL 0xC0
+ mask MANACKDLY 0x3F
+}
+
+/*
+ * Data FIFO Data
+ * Read/Write byte port into the data FIFO. The read and write
+ * FIFO pointers increment with each read and write respectively
+ * to this port.
+ */
+register DFDAT {
+ address 0x0C4
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * DSP Channel Select
+ */
+register DSPSELECT {
+ address 0x0C4
+ access_mode RW
+ modes M_CFG
+ bit AUTOINCEN 0x80
+ mask DSPSEL 0x1F
+}
+
+const NUMDSPS 0x14
+
+/*
+ * Write Bias Control
+ */
+register WRTBIASCTL {
+ address 0x0C5
+ access_mode WO
+ modes M_CFG
+ bit AUTOXBCDIS 0x80
+ mask XMITMANVAL 0x3F
+}
+
+const WRTBIASCTL_CPQ_DEFAULT 0x97
+
+/*
+ * Receiver Bias Control
+ */
+register RCVRBIOSCTL {
+ address 0x0C6
+ access_mode WO
+ modes M_CFG
+ bit AUTORBCDIS 0x80
+ mask RCVRMANVAL 0x3F
+}
+
+/*
+ * Write Bias Calculator
+ */
+register WRTBIASCALC {
+ address 0x0C7
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * Data FIFO Pointers
+ * Contains the byte offset from DFWADDR and DWRADDR to the current
+ * FIFO write/read locations.
+ */
+register DFPTRS {
+ address 0x0C8
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Receiver Bias Calculator
+ */
+register RCVRBIASCALC {
+ address 0x0C8
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * Data FIFO Debug Control
+ */
+register DFDBCTL {
+ address 0x0C8
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ bit DFF_CIO_WR_RDY 0x20
+ bit DFF_CIO_RD_RDY 0x10
+ bit DFF_DIR_ERR 0x08
+ bit DFF_RAMBIST_FAIL 0x04
+ bit DFF_RAMBIST_DONE 0x02
+ bit DFF_RAMBIST_EN 0x01
+}
+
+/*
+ * Data FIFO Backup Read Pointer
+ * Contains the data FIFO address to be restored if the last
+ * data accessed from the data FIFO was not transferred successfully.
+ */
+register DFBKPTR {
+ address 0x0C9
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Skew Calculator
+ */
+register SKEWCALC {
+ address 0x0C9
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * Data FIFO Space Count
+ * Number of FIFO locations that are free.
+ */
+register DFSCNT {
+ address 0x0CC
+ access_mode RO
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Data FIFO Byte Count
+ * Number of filled FIFO locations.
+ */
+register DFBCNT {
+ address 0x0CE
+ access_mode RO
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Sequencer Program Overlay Address.
+ * Low address must be written prior to high address.
+ */
+register OVLYADDR {
+ address 0x0D4
+ modes M_SCSI
+ size 2
+ access_mode RW
+}
+
+/*
+ * Sequencer Control 0
+ * Error detection mode, speed configuration,
+ * single step, breakpoints and program load.
+ */
+register SEQCTL0 {
+ address 0x0D6
+ access_mode RW
+ bit PERRORDIS 0x80
+ bit PAUSEDIS 0x40
+ bit FAILDIS 0x20
+ bit FASTMODE 0x10
+ bit BRKADRINTEN 0x08
+ bit STEP 0x04
+ bit SEQRESET 0x02
+ bit LOADRAM 0x01
+}
+
+/*
+ * Sequencer Control 1
+ * Instruction RAM Diagnostics
+ */
+register SEQCTL1 {
+ address 0x0D7
+ access_mode RW
+ bit OVRLAY_DATA_CHK 0x08
+ bit RAMBIST_DONE 0x04
+ bit RAMBIST_FAIL 0x02
+ bit RAMBIST_EN 0x01
+}
+
+/*
+ * Sequencer Flags
+ * Zero and Carry state of the ALU.
+ */
+register FLAGS {
+ address 0x0D8
+ access_mode RO
+ bit ZERO 0x02
+ bit CARRY 0x01
+}
+
+/*
+ * Sequencer Interrupt Control
+ */
+register SEQINTCTL {
+ address 0x0D9
+ access_mode RW
+ bit INTVEC1DSL 0x80
+ bit INT1_CONTEXT 0x20
+ bit SCS_SEQ_INT1M1 0x10
+ bit SCS_SEQ_INT1M0 0x08
+ mask INTMASK 0x06
+ bit IRET 0x01
+}
+
+/*
+ * Sequencer RAM Data Port
+ * Single byte window into the Sequencer Instruction Ram area starting
+ * at the address specified by OVLYADDR. To write a full instruction word,
+ * simply write four bytes in succession. OVLYADDR will increment after the
+ * most significant instrution byte (the byte with the parity bit) is written.
+ */
+register SEQRAM {
+ address 0x0DA
+ access_mode RW
+}
+
+/*
+ * Sequencer Program Counter
+ * Low byte must be written prior to high byte.
+ */
+register PRGMCNT {
+ address 0x0DE
+ access_mode RW
+ size 2
+}
+
+/*
+ * Accumulator
+ */
+register ACCUM {
+ address 0x0E0
+ access_mode RW
+ accumulator
+}
+
+/*
+ * Source Index Register
+ * Incrementing index for reads of SINDIR and the destination (low byte only)
+ * for any immediate operands passed in jmp, jc, jnc, call instructions.
+ * Example:
+ * mvi 0xFF call some_routine;
+ *
+ * Will set SINDEX[0] to 0xFF and call the routine "some_routine.
+ */
+register SINDEX {
+ address 0x0E2
+ access_mode RW
+ size 2
+ sindex
+}
+
+/*
+ * Destination Index Register
+ * Incrementing index for writes to DINDIR. Can be used as a scratch register.
+ */
+register DINDEX {
+ address 0x0E4
+ access_mode RW
+ size 2
+}
+
+/*
+ * Break Address
+ * Sequencer instruction breakpoint address address.
+ */
+register BRKADDR0 {
+ address 0x0E6
+ access_mode RW
+}
+
+register BRKADDR1 {
+ address 0x0E6
+ access_mode RW
+ bit BRKDIS 0x80 /* Disable Breakpoint */
+}
+
+/*
+ * All Ones
+ * All reads to this register return the value 0xFF.
+ */
+register ALLONES {
+ address 0x0E8
+ access_mode RO
+ allones
+}
+
+/*
+ * All Zeros
+ * All reads to this register return the value 0.
+ */
+register ALLZEROS {
+ address 0x0EA
+ access_mode RO
+ allzeros
+}
+
+/*
+ * No Destination
+ * Writes to this register have no effect.
+ */
+register NONE {
+ address 0x0EA
+ access_mode WO
+ none
+}
+
+/*
+ * Source Index Indirect
+ * Reading this register is equivalent to reading (register_base + SINDEX) and
+ * incrementing SINDEX by 1.
+ */
+register SINDIR {
+ address 0x0EC
+ access_mode RO
+}
+
+/*
+ * Destination Index Indirect
+ * Writing this register is equivalent to writing to (register_base + DINDEX)
+ * and incrementing DINDEX by 1.
+ */
+register DINDIR {
+ address 0x0ED
+ access_mode WO
+}
+
+/*
+ * Function One
+ * 2's complement to bit value conversion. Write the 2's complement value
+ * (0-7 only) to the top nibble and retrieve the bit indexed by that value
+ * on the next read of this register.
+ * Example:
+ * Write 0x60
+ * Read 0x40
+ */
+register FUNCTION1 {
+ address 0x0F0
+ access_mode RW
+}
+
+/*
+ * Stack
+ * Window into the stack. Each stack location is 10 bits wide reported
+ * low byte followed by high byte. There are 8 stack locations.
+ */
+register STACK {
+ address 0x0F2
+ access_mode RW
+}
+
+/*
+ * Interrupt Vector 1 Address
+ * Interrupt branch address for SCS SEQ_INT1 mode 0 and 1 interrupts.
+ */
+register INTVEC1_ADDR {
+ address 0x0F4
+ access_mode RW
+ size 2
+ modes M_CFG
+}
+
+/*
+ * Current Address
+ * Address of the SEQRAM instruction currently executing instruction.
+ */
+register CURADDR {
+ address 0x0F4
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Interrupt Vector 2 Address
+ * Interrupt branch address for HST_SEQ_INT2 interrupts.
+ */
+register INTVEC2_ADDR {
+ address 0x0F6
+ access_mode RW
+ size 2
+ modes M_CFG
+}
+
+/*
+ * Last Address
+ * Address of the SEQRAM instruction executed prior to the current instruction.
+ */
+register LASTADDR {
+ address 0x0F6
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+register AHD_PCI_CONFIG_BASE {
+ address 0x100
+ access_mode RW
+ size 256
+ modes M_CFG
+}
+
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+scratch_ram {
+ /* Mode Specific */
+ address 0x0A0
+ size 8
+ modes 0, 1, 2, 3
+ REG0 {
+ size 2
+ }
+ REG1 {
+ size 2
+ }
+ REG2 {
+ size 2
+ }
+ SG_STATE {
+ size 1
+ bit SEGS_AVAIL 0x01
+ bit LOADING_NEEDED 0x02
+ bit FETCH_INPROG 0x04
+ }
+ /*
+ * Track whether the transfer byte count for
+ * the current data phase is odd.
+ */
+ DATA_COUNT_ODD {
+ size 1
+ }
+}
+
+scratch_ram {
+ /* Mode Specific */
+ address 0x0F8
+ size 8
+ modes 0, 1, 2, 3
+ LONGJMP_ADDR {
+ size 2
+ }
+ LONGJMP_SCB {
+ size 2
+ }
+ ACCUM_SAVE {
+ size 1
+ }
+}
+
+
+scratch_ram {
+ address 0x100
+ size 128
+ modes 0, 1, 2, 3
+ /*
+ * Per "other-id" execution queues. We use an array of
+ * tail pointers into lists of SCBs sorted by "other-id".
+ * The execution head pointer threads the head SCBs for
+ * each list.
+ */
+ WAITING_SCB_TAILS {
+ size 32
+ }
+ WAITING_TID_HEAD {
+ size 2
+ }
+ WAITING_TID_TAIL {
+ size 2
+ }
+ /*
+ * SCBID of the next SCB in the new SCB queue.
+ */
+ NEXT_QUEUED_SCB_ADDR {
+ size 4
+ }
+ /*
+ * head of list of SCBs that have
+ * completed but have not been
+ * put into the qoutfifo.
+ */
+ COMPLETE_SCB_HEAD {
+ size 2
+ }
+ /*
+ * The list of completed SCBs in
+ * the active DMA.
+ */
+ COMPLETE_SCB_DMAINPROG_HEAD {
+ size 2
+ }
+ /*
+ * head of list of SCBs that have
+ * completed but need to be uploaded
+ * to the host prior to being completed.
+ */
+ COMPLETE_DMA_SCB_HEAD {
+ size 2
+ }
+ /* Counting semaphore to prevent new select-outs */
+ QFREEZE_COUNT {
+ size 2
+ }
+ /*
+ * Mode to restore on idle_loop exit.
+ */
+ SAVED_MODE {
+ size 1
+ }
+ /*
+ * Single byte buffer used to designate the type or message
+ * to send to a target.
+ */
+ MSG_OUT {
+ size 1
+ }
+ /* Parameters for DMA Logic */
+ DMAPARAMS {
+ size 1
+ bit PRELOADEN 0x80
+ bit WIDEODD 0x40
+ bit SCSIEN 0x20
+ bit SDMAEN 0x10
+ bit SDMAENACK 0x10
+ bit HDMAEN 0x08
+ bit HDMAENACK 0x08
+ bit DIRECTION 0x04 /* Set indicates PCI->SCSI */
+ bit FIFOFLUSH 0x02
+ bit FIFORESET 0x01
+ }
+ SEQ_FLAGS {
+ size 1
+ bit NOT_IDENTIFIED 0x80
+ bit TARGET_CMD_IS_TAGGED 0x40
+ bit NO_CDB_SENT 0x40
+ bit DPHASE 0x20
+ /* Target flags */
+ bit TARG_CMD_PENDING 0x10
+ bit CMDPHASE_PENDING 0x08
+ bit DPHASE_PENDING 0x04
+ bit SPHASE_PENDING 0x02
+ bit NO_DISCONNECT 0x01
+ }
+ /*
+ * Temporary storage for the
+ * target/channel/lun of a
+ * reconnecting target
+ */
+ SAVED_SCSIID {
+ size 1
+ }
+ SAVED_LUN {
+ size 1
+ }
+ /*
+ * The last bus phase as seen by the sequencer.
+ */
+ LASTPHASE {
+ size 1
+ bit CDI 0x80
+ bit IOI 0x40
+ bit MSGI 0x20
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_DATAOUT_DT P_DATAOUT|MSGO
+ mask P_DATAIN_DT P_DATAIN|MSGO
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+ mask P_BUSFREE 0x01
+ }
+ /*
+ * Base address of our shared data with the kernel driver in host
+ * memory. This includes the qoutfifo and target mode
+ * incoming command queue.
+ */
+ SHARED_DATA_ADDR {
+ size 4
+ }
+ /*
+ * Pointer to location in host memory for next
+ * position in the qoutfifo.
+ */
+ QOUTFIFO_NEXT_ADDR {
+ size 4
+ }
+ /*
+ * Kernel and sequencer offsets into the queue of
+ * incoming target mode command descriptors. The
+ * queue is full when the KERNEL_TQINPOS == TQINPOS.
+ */
+ KERNEL_TQINPOS {
+ size 1
+ }
+ TQINPOS {
+ size 1
+ }
+ ARG_1 {
+ size 1
+ mask SEND_MSG 0x80
+ mask SEND_SENSE 0x40
+ mask SEND_REJ 0x20
+ mask MSGOUT_PHASEMIS 0x10
+ mask EXIT_MSG_LOOP 0x08
+ mask CONT_MSG_LOOP_WRITE 0x04
+ mask CONT_MSG_LOOP_READ 0x03
+ mask CONT_MSG_LOOP_TARG 0x02
+ alias RETURN_1
+ }
+ ARG_2 {
+ size 1
+ alias RETURN_2
+ }
+
+ /*
+ * Snapshot of MSG_OUT taken after each message is sent.
+ */
+ LAST_MSG {
+ size 1
+ }
+
+ /*
+ * Sequences the kernel driver has okayed for us. This allows
+ * the driver to do things like prevent initiator or target
+ * operations.
+ */
+ SCSISEQ_TEMPLATE {
+ size 1
+ bit MANUALCTL 0x40
+ bit ENSELI 0x20
+ bit ENRSELI 0x10
+ mask MANUALP 0x0C
+ bit ENAUTOATNP 0x02
+ bit ALTSTIM 0x01
+ }
+
+ /*
+ * The initiator specified tag for this target mode transaction.
+ */
+ INITIATOR_TAG {
+ size 1
+ }
+
+ SEQ_FLAGS2 {
+ size 1
+ bit SCB_DMA 0x01
+ bit TARGET_MSG_PENDING 0x02
+ bit SELECTOUT_QFROZEN 0x04
+ }
+ /*
+ * Target-mode CDB type to CDB length table used
+ * in non-packetized operation.
+ */
+ CMDSIZE_TABLE {
+ size 8
+ }
+}
+
+/************************* Hardware SCB Definition ****************************/
+scb {
+ address 0x180
+ size 64
+ modes 0, 1, 2, 3
+ SCB_RESIDUAL_DATACNT {
+ size 4
+ alias SCB_CDB_STORE
+ }
+ SCB_RESIDUAL_SGPTR {
+ size 4
+ alias SCB_CDB_PTR
+ mask SG_ADDR_MASK 0xf8 /* In the last byte */
+ bit SG_OVERRUN_RESID 0x02 /* In the first byte */
+ bit SG_LIST_NULL 0x01 /* In the first byte */
+ }
+ SCB_SCSI_STATUS {
+ size 1
+ }
+ SCB_TARGET_PHASES {
+ size 1
+ }
+ SCB_TARGET_DATA_DIR {
+ size 1
+ }
+ SCB_TARGET_ITAG {
+ size 1
+ }
+ SCB_SENSE_BUSADDR {
+ /*
+ * Only valid if CDB length is less than 13 bytes or
+ * we are using a CDB pointer. Otherwise contains
+ * the last 4 bytes of embedded cdb information.
+ */
+ size 4
+ alias SCB_NEXT_COMPLETE
+ }
+ SCB_CDB_LEN {
+ size 1
+ bit SCB_CDB_LEN_PTR 0x80 /* CDB in host memory */
+ }
+ SCB_TASK_MANAGEMENT {
+ size 1
+ }
+ SCB_TAG {
+ size 2
+ }
+ SCB_NEXT {
+ alias SCB_NEXT_SCB_BUSADDR
+ size 2
+ }
+ SCB_NEXT2 {
+ size 2
+ }
+ SCB_DATAPTR {
+ size 8
+ }
+ SCB_DATACNT {
+ /*
+ * The last byte is really the high address bits for
+ * the data address.
+ */
+ size 4
+ bit SG_LAST_SEG 0x80 /* In the fourth byte */
+ mask SG_HIGH_ADDR_BITS 0x7F /* In the fourth byte */
+ }
+ SCB_SGPTR {
+ size 4
+ bit SG_STATUS_VALID 0x04 /* In the first byte */
+ bit SG_FULL_RESID 0x02 /* In the first byte */
+ bit SG_LIST_NULL 0x01 /* In the first byte */
+ }
+ SCB_CONTROL {
+ size 1
+ bit TARGET_SCB 0x80
+ bit DISCENB 0x40
+ bit TAG_ENB 0x20
+ bit MK_MESSAGE 0x10
+ bit STATUS_RCVD 0x08
+ bit DISCONNECTED 0x04
+ mask SCB_TAG_TYPE 0x03
+ }
+ SCB_SCSIID {
+ size 1
+ mask TID 0xF0
+ mask OID 0x0F
+ }
+ SCB_LUN {
+ size 1
+ mask LID 0xff
+ }
+ SCB_TASK_ATTRIBUTE {
+ size 1
+ alias SCB_NONPACKET_TAG
+ }
+ SCB_BUSADDR {
+ size 4
+ }
+ SCB_DISCONNECTED_LISTS {
+ size 16
+ }
+}
+
+/*********************************** Constants ********************************/
+const SEQ_STACK_SIZE 8
+const MK_MESSAGE_BIT_OFFSET 4
+const TID_SHIFT 4
+const TARGET_CMD_CMPLT 0xfe
+const INVALID_ADDR 0x80
+#define SCB_LIST_NULL 0xff
+
+const CCSGADDR_MAX 0x80
+const CCSCBADDR_MAX 0x80
+const CCSGRAM_MAXSEGS 16
+
+/* Selection Timeout Timer Constants */
+const STIMESEL_SHIFT 3
+const STIMESEL_MIN 0x18
+const STIMESEL_BUG_ADJ 0x8
+
+/* WDTR Message values */
+const BUS_8_BIT 0x00
+const BUS_16_BIT 0x01
+const BUS_32_BIT 0x02
+
+/* Offset maximums */
+const MAX_OFFSET 0xfe
+const MAX_OFFSET_PACED 0x7f
+const HOST_MSG 0xff
+
+/*
+ * The size of our sense buffers.
+ * Sense buffer mapping can be handled in either of two ways.
+ * The first is to allocate a dmamap for each transaction.
+ * Depending on the architecture, dmamaps can be costly. The
+ * alternative is to statically map the buffers in much the same
+ * way we handle our scatter gather lists. The driver implements
+ * the later.
+ */
+const AHD_SENSE_BUFSIZE 256
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT 0x05
+
+const STATUS_BUSY 0x08
+const STATUS_QUEUE_FULL 0x28
+const STATUS_PKT_SENSE 0xFF
+const TARGET_DATA_IN 1
+
+const SCB_TRANSFER_SIZE 48
+/* PKT_OVERRUN_BUFSIZE must be a multiple of 256 less than 64K */
+const PKT_OVERRUN_BUFSIZE 512
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+const SG_PREFETCH_CNT download
+const SG_PREFETCH_CNT_LIMIT download
+const SG_PREFETCH_ALIGN_MASK download
+const SG_PREFETCH_ADDR_MASK download
+const SG_SIZEOF download
+const PKT_OVERRUN_BUFOFFSET download
+
+/*
+ * BIOS SCB offsets
+ */
+const NVRAM_SCB_OFFSET 0x2C
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx.seq b/xen/drivers/scsi/aic7xxx/aic79xx.seq
new file mode 100644
index 0000000000..0fb657516c
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx.seq
@@ -0,0 +1,1723 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#42 $"
+PATCH_ARG_LIST = "struct ahd_softc *ahd"
+
+#include "aic79xx.reg"
+#include "scsi_message.h"
+
+idle_loop:
+ SET_MODE(M_SCSI, M_SCSI);
+ test SCSISEQ0, ENSELO|ENARBO jnz idle_loop_checkbus;
+ test SEQ_FLAGS2, SELECTOUT_QFROZEN jnz idle_loop_checkbus;
+ cmp WAITING_TID_HEAD[1], SCB_LIST_NULL je idle_loop_checkbus;
+ /*
+ * ENSELO is cleared by a SELDO, so we must test for SELDO
+ * one last time.
+ */
+BEGIN_CRITICAL;
+ test SSTAT0, SELDO jnz select_out;
+END_CRITICAL;
+ call start_selection;
+idle_loop_checkbus:
+BEGIN_CRITICAL;
+ test SSTAT0, SELDO jnz select_out;
+END_CRITICAL;
+ test SSTAT0, SELDI jnz select_in;
+ test SCSIPHASE, ~DATA_PHASE_MASK jz idle_loop_check_nonpackreq;
+ test SCSISIGO, ATNO jz idle_loop_check_nonpackreq;
+ call unexpected_nonpkt_phase_find_ctxt;
+idle_loop_check_nonpackreq:
+ test SSTAT2, NONPACKREQ jz idle_loop_scsi;
+ call unexpected_nonpkt_phase_find_ctxt;
+idle_loop_scsi:
+BEGIN_CRITICAL;
+ test LQISTAT2, LQIGSAVAIL jz idle_loop_service_fifos;
+ /*
+ * We have received good status for this transaction. There may
+ * still be data in our FIFOs draining to the host. Setup
+ * monitoring of the draining process or complete the SCB.
+ */
+good_status_IU_done:
+ bmov SCBPTR, GSFIFO, 2;
+ clr SCB_SCSI_STATUS;
+ or SCB_CONTROL, STATUS_RCVD;
+
+ /*
+ * Since this status did not consume a FIFO, we have to
+ * be a bit more dilligent in how we check for FIFOs pertaining
+ * to this transaction. There are three states that a FIFO still
+ * transferring data may be in.
+ *
+ * 1) Configured and draining to the host, with a pending CLRCHN.
+ * 2) Configured and draining to the host, no pending CLRCHN.
+ * 3) Pending cfg4data, fifo not empty.
+ *
+ * For case 1, we assume that our DMA post of the completed command
+ * will occur after the FIFO finishes draining due to the higher
+ * priority of data FIFO transfers relative to command channel
+ * transfers.
+ *
+ * Case 2 can be detected by noticing that a longjmp is active for the
+ * FIFO and LONGJMP_SCB matches our SCB. In this case, we allow
+ * the routine servicing the FIFO to complete the SCB.
+ *
+ * Case 3 implies either a pending or yet to occur save data
+ * pointers for this same context in the other FIFO. So, if
+ * we detect case 2, we will properly defer the post of the SCB
+ * and achieve the desired result. The pending cfg4data will
+ * notice that status has been received and complete the SCB.
+ */
+ test SCB_SGPTR, SG_LIST_NULL jz good_status_check_fifos;
+ /*
+ * All segments have been loaded (or no data transfer), so
+ * it is safe to complete the command. Since this was a
+ * cheap command to check for completion, loop to see if
+ * more entries can be removed from the GSFIFO.
+ */
+ call complete;
+END_CRITICAL;
+ jmp idle_loop_scsi;
+BEGIN_CRITICAL;
+good_status_check_fifos:
+ clc;
+ bmov ARG_1, SCBPTR, 2;
+ SET_MODE(M_DFF0, M_DFF0);
+ call check_fifo;
+ jc idle_loop_service_fifos;
+ SET_MODE(M_DFF1, M_DFF1);
+ call check_fifo;
+ jc idle_loop_service_fifos;
+ SET_MODE(M_SCSI, M_SCSI);
+ call queue_scb_completion;
+END_CRITICAL;
+idle_loop_service_fifos:
+ SET_MODE(M_DFF0, M_DFF0);
+ test LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_next_fifo;
+ call longjmp;
+idle_loop_next_fifo:
+ SET_MODE(M_DFF1, M_DFF1);
+ test LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_last_fifo_done;
+ call longjmp;
+idle_loop_last_fifo_done:
+ call idle_loop_cchan;
+ jmp idle_loop;
+
+idle_loop_cchan:
+ SET_MODE(M_CCHAN, M_CCHAN);
+ test CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle;
+ test CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog;
+ test CCSCBCTL, CCSCBDONE jz return;
+ /* FALLTHROUGH */
+scbdma_tohost_done:
+ test CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
+ /*
+ * A complete SCB upload requires no intervention.
+ * The SCB is already on the COMPLETE_SCB list
+ * and its completion notification will now be
+ * handled just like any other SCB.
+ */
+ and CCSCBCTL, ~(CCARREN|CCSCBEN) ret;
+fill_qoutfifo_dmadone:
+ and CCSCBCTL, ~(CCARREN|CCSCBEN);
+ mvi INTSTAT, CMDCMPLT;
+ mvi COMPLETE_SCB_DMAINPROG_HEAD[1], SCB_LIST_NULL;
+ bmov QOUTFIFO_NEXT_ADDR, SCBHADDR, 4;
+ test QOFF_CTLSTA, SDSCB_ROLLOVR jz return;
+ bmov QOUTFIFO_NEXT_ADDR, SHARED_DATA_ADDR, 4 ret;
+
+fetch_new_scb_inprog:
+ test CCSCBCTL, ARRDONE jz return;
+fetch_new_scb_done:
+ and CCSCBCTL, ~(CCARREN|CCSCBEN);
+ bmov REG0, SCBPTR, 2;
+ /* Update the next SCB address to download. */
+ bmov NEXT_QUEUED_SCB_ADDR, SCB_NEXT_SCB_BUSADDR, 4;
+ mvi SCB_NEXT[1], SCB_LIST_NULL;
+ mvi SCB_NEXT2[1], SCB_LIST_NULL;
+ /*
+ * SCBs that want to send messages are always
+ * queued independently. This ensures that they
+ * are at the head of the SCB list to select out
+ * to a target and we will see the MK_MESSAGE flag.
+ */
+ test SCB_CONTROL, MK_MESSAGE jnz first_new_target_scb;
+ shr SINDEX, 3, SCB_SCSIID;
+ and SINDEX, ~0x1;
+ mvi SINDEX[1], (WAITING_SCB_TAILS >> 8);
+ bmov DINDEX, SINDEX, 2;
+ bmov SCBPTR, SINDIR, 2;
+ bmov DINDIR, REG0, 2;
+ cmp SCBPTR[1], SCB_LIST_NULL je first_new_target_scb;
+ bmov SCB_NEXT, REG0, 2;
+fetch_new_scb_fini:
+ /* Increment our position in the QINFIFO. */
+ mov NONE, SNSCB_QOFF ret;
+first_new_target_scb:
+ cmp WAITING_TID_HEAD[1], SCB_LIST_NULL je first_new_scb;
+ bmov SCBPTR, WAITING_TID_TAIL, 2;
+ bmov SCB_NEXT2, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2;
+ /* Increment our position in the QINFIFO. */
+ mov NONE, SNSCB_QOFF ret;
+first_new_scb:
+ bmov WAITING_TID_HEAD, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2;
+ /* Increment our position in the QINFIFO. */
+ mov NONE, SNSCB_QOFF ret;
+
+scbdma_idle:
+ /*
+ * Give precedence to downloading new SCBs to execute
+ * unless select-outs are currently frozen.
+ * XXX Use a timer to prevent completion starvation.
+ */
+ test SEQ_FLAGS2, SELECTOUT_QFROZEN jnz . + 2;
+BEGIN_CRITICAL;
+ test QOFF_CTLSTA, NEW_SCB_AVAIL jnz fetch_new_scb;
+ cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne dma_complete_scb;
+ cmp COMPLETE_SCB_HEAD[1], SCB_LIST_NULL je return;
+ /* FALLTHROUGH */
+fill_qoutfifo:
+ /*
+ * Keep track of the SCBs we are dmaing just
+ * in case the DMA fails or is aborted.
+ */
+ bmov COMPLETE_SCB_DMAINPROG_HEAD, COMPLETE_SCB_HEAD, 2;
+ mvi CCSCBCTL, CCSCBRESET;
+ bmov SCBHADDR, QOUTFIFO_NEXT_ADDR, 4;
+ bmov CCSCBRAM, COMPLETE_SCB_HEAD, 2;
+ bmov SCBPTR, COMPLETE_SCB_HEAD, 2;
+ jmp fill_qoutfifo_first_entry;
+fill_qoutfifo_loop:
+ bmov CCSCBRAM, SCB_NEXT_COMPLETE, 2;
+ bmov SCBPTR, SCB_NEXT_COMPLETE, 2;
+fill_qoutfifo_first_entry:
+ mov NONE, SDSCB_QOFF;
+ cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je fill_qoutfifo_done;
+ cmp CCSCBADDR, CCSCBADDR_MAX je fill_qoutfifo_done;
+ test QOFF_CTLSTA, SDSCB_ROLLOVR jz fill_qoutfifo_loop;
+fill_qoutfifo_done:
+ mov SCBHCNT, CCSCBADDR;
+ mvi CCSCBCTL, CCSCBEN|CCSCBRESET;
+ bmov COMPLETE_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+ mvi SCB_NEXT_COMPLETE[1], SCB_LIST_NULL ret;
+
+fetch_new_scb:
+ bmov SCBHADDR, NEXT_QUEUED_SCB_ADDR, 4;
+ mvi CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET jmp dma_scb;
+dma_complete_scb:
+ bmov SCBPTR, COMPLETE_DMA_SCB_HEAD, 2;
+ bmov SCBHADDR, SCB_BUSADDR, 4;
+ mvi CCARREN|CCSCBEN|CCSCBRESET call dma_scb;
+ /*
+ * Now that we've started the DMA, push us onto
+ * the normal completion queue to have our SCBID
+ * posted to the kernel.
+ */
+ bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+ bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+ bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
+END_CRITICAL;
+
+/*
+ * Either post or fetch an SCB from host memory. The caller
+ * is responsible for polling for transfer completion.
+ *
+ * Prerequisits: Mode == M_CCHAN
+ * SINDEX contains CCSCBCTL flags
+ * SCBHADDR set to Host SCB address
+ * SCBPTR set to SCB src location on "push" operations
+ */
+SET_SRC_MODE M_CCHAN;
+SET_DST_MODE M_CCHAN;
+dma_scb:
+ mvi SCBHCNT, SCB_TRANSFER_SIZE;
+ mov CCSCBCTL, SINDEX;
+ or SEQ_FLAGS2, SCB_DMA ret;
+
+BEGIN_CRITICAL;
+setjmp_setscb:
+ bmov LONGJMP_SCB, SCBPTR, 2;
+setjmp:
+ bmov LONGJMP_ADDR, STACK, 2 ret;
+setjmp_inline:
+ bmov LONGJMP_ADDR, STACK, 2;
+longjmp:
+ bmov STACK, LONGJMP_ADDR, 2 ret;
+END_CRITICAL;
+
+/************************ Packetized LongJmp Routines *************************/
+/*
+ * Must disable interrupts when setting the mode pointer
+ * register as an interrupt occurring mid update will
+ * fail to store the new mode value for restoration on
+ * an iret.
+ */
+set_mode_work_around:
+ mvi SEQINTCTL, INTVEC1DSL;
+ mov MODE_PTR, SINDEX;
+ clr SEQINTCTL ret;
+
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+start_selection:
+BEGIN_CRITICAL;
+ if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+ /*
+ * Razor #494
+ * Rev A hardware fails to update LAST/CURR/NEXTSCB
+ * correctly after a packetized selection in several
+ * situations:
+ *
+ * 1) If only one command existed in the the queue, the
+ * LAST/CURR/NEXTSCB are unchanged.
+ *
+ * 2) In a non QAS, protocol allowed phase change,
+ * the queue is shifted 1 too far. LASTSCB is
+ * the last SCB that was correctly processed.
+ *
+ * 3) In the QAS case, if the full list of commands
+ * was successfully sent, NEXTSCB is NULL and neither
+ * CURRSCB nor LASTSCB can be trusted. We must
+ * manually walk the list counting MAXCMDCNT elements
+ * to find the last SCB that was sent correctly.
+ *
+ * To simplify the workaround for this bug in SELDO
+ * handling, we initialize LASTSCB prior to enabling
+ * selection so we can rely on it even for case #1 above.
+ */
+ bmov LASTSCB, WAITING_TID_HEAD, 2;
+ }
+ bmov CURRSCB, WAITING_TID_HEAD, 2;
+ bmov SCBPTR, WAITING_TID_HEAD, 2;
+ shr SELOID, 4, SCB_SCSIID;
+ /*
+ * If we want to send a message to the device, ensure
+ * we are selecting with atn irregardless of our packetized
+ * agreement. Since SPI4 only allows target reset or PPR
+ * messages if this is a packetized connection, the change
+ * to our negotiation table entry for this selection will
+ * be cleared when the message is acted on.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz . + 3;
+ mov NEGOADDR, SELOID;
+ or NEGCONOPTS, ENAUTOATNO;
+ or SCSISEQ0, ENSELO ret;
+END_CRITICAL;
+
+/*
+ * Allocate a FIFO for a non-packetized transaction.
+ * For some reason unkown to me, both FIFOs must be free before we
+ * can allocate a FIFO for a non-packetized transaction. This
+ * may be fixed in Rev B.
+ */
+allocate_fifo_loop:
+ /*
+ * Do whatever work is required to free a FIFO.
+ */
+ SET_MODE(M_DFF0, M_DFF0);
+ test LONGJMP_ADDR[1], INVALID_ADDR jnz . + 2;
+ call longjmp;
+ SET_MODE(M_DFF1, M_DFF1);
+ test LONGJMP_ADDR[1], INVALID_ADDR jnz . + 2;
+ call longjmp;
+ SET_MODE(M_SCSI, M_SCSI);
+allocate_fifo:
+ and A, FIFO0FREE|FIFO1FREE, DFFSTAT;
+ cmp A, FIFO0FREE|FIFO1FREE jne allocate_fifo_loop;
+take_fifo:
+ bmov ARG_1, SCBPTR, 2;
+ or DFFSTAT, CURRFIFO;
+ SET_MODE(M_DFF1, M_DFF1);
+ bmov SCBPTR, ARG_1, 2 ret;
+
+/*
+ * We have been reselected as an initiator
+ * or selected as a target.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+select_in:
+ or SXFRCTL0, SPIOEN;
+ and SAVED_SCSIID, SELID_MASK, SELID;
+ and A, OID, IOWNID;
+ or SAVED_SCSIID, A;
+ mvi CLRSINT0, CLRSELDI;
+ jmp ITloop;
+
+/*
+ * We have successfully selected out.
+ *
+ * Clear SELDO.
+ * Dequeue all SCBs sent from the waiting queue
+ * Requeue all SCBs *not* sent to the tail of the waiting queue
+ * Take Razor #494 into account for above.
+ *
+ * In Packetized Mode:
+ * Return to the idle loop. Our interrupt handler will take
+ * care of any incoming L_Qs.
+ *
+ * In Non-Packetize Mode:
+ * Continue to our normal state machine.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+select_out:
+BEGIN_CRITICAL;
+ /* Clear out all SCBs that have been successfully sent. */
+ if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+ /*
+ * For packetized, the LQO manager clears ENSELO on
+ * the assertion of SELDO. If we are non-packetized,
+ * LASTSCB and CURRSCB are acuate.
+ */
+ test SCSISEQ0, ENSELO jnz use_lastscb;
+
+ /*
+ * The update is correct for LQOSTAT1 errors. All
+ * but LQOBUSFREE are handled by kernel interrupts.
+ * If we see LQOBUSFREE, return to the idle loop.
+ * Once we are out of the select_out critical section,
+ * the kernel will cleanup the LQOBUSFREE and we will
+ * eventually restart the selection if appropriate.
+ */
+ test LQOSTAT1, LQOBUSFREE jnz idle_loop;
+
+ /*
+ * On a phase change oustside of packet boundaries,
+ * LASTSCB points to the currently active SCB context
+ * on the bus.
+ */
+ test LQOSTAT2, LQOPHACHGOUTPKT jnz use_lastscb;
+
+ /*
+ * If the hardware has traversed the whole list, NEXTSCB
+ * will be NULL, CURRSCB and LASTSCB cannot be trusted,
+ * but MAXCMDCNT is accurate. If we stop part way through
+ * the list or only had one command to issue, NEXTSCB[1] is
+ * not NULL and LASTSCB is the last command to go out.
+ */
+ cmp NEXTSCB[1], SCB_LIST_NULL jne use_lastscb;
+
+ /*
+ * Brute force walk.
+ */
+ bmov SCBPTR, WAITING_TID_HEAD, 2;
+ mvi SEQINTCTL, INTVEC1DSL;
+ mvi MODE_PTR, MK_MODE(M_CFG, M_CFG);
+ mov A, MAXCMDCNT;
+ mvi MODE_PTR, MK_MODE(M_SCSI, M_SCSI);
+ clr SEQINTCTL;
+find_lastscb_loop:
+ dec A;
+ test A, 0xFF jz found_last_sent_scb;
+ bmov SCBPTR, SCB_NEXT, 2;
+ jmp find_lastscb_loop;
+use_lastscb:
+ bmov SCBPTR, LASTSCB, 2;
+found_last_sent_scb:
+ bmov CURRSCB, SCBPTR, 2;
+curscb_ww_done:
+ } else {
+ /*
+ * Untested - Verify with Rev B.
+ */
+ bmov SCBPTR, CURRSCB, 2;
+ }
+ /*
+ * Requeue any SCBs not sent, to the tail of the waiting Q.
+ */
+ cmp SCB_NEXT[1], SCB_LIST_NULL je select_out_list_done;
+
+ /*
+ * We know that neither the per-TID list nor the list of
+ * TIDs is empty. Use this knowledge to our advantage.
+ */
+ bmov REG0, SCB_NEXT, 2;
+ bmov SCBPTR, WAITING_TID_TAIL, 2;
+ bmov SCB_NEXT2, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2;
+ jmp select_out_inc_tid_q;
+
+select_out_list_done:
+ /*
+ * The whole list made it. Just clear our TID's tail pointer
+ * unless we were queued independently due to our need to
+ * send a message.
+ */
+ test SCB_CONTROL, MK_MESSAGE jnz select_out_inc_tid_q;
+ shr DINDEX, 3, SCB_SCSIID;
+/* XXX When we switch to SCB_SELOID, put +1 in addition below. */
+ or DINDEX, 1; /* Want only the second byte */
+ mvi DINDEX[1], ((WAITING_SCB_TAILS) >> 8);
+ mvi DINDIR, SCB_LIST_NULL;
+select_out_inc_tid_q:
+ bmov SCBPTR, WAITING_TID_HEAD, 2;
+ bmov WAITING_TID_HEAD, SCB_NEXT2, 2;
+ cmp WAITING_TID_HEAD[1], SCB_LIST_NULL jne . + 2;
+ mvi WAITING_TID_TAIL[1], SCB_LIST_NULL;
+ bmov SCBPTR, CURRSCB, 2;
+END_CRITICAL;
+
+ mvi CLRSINT0, CLRSELDO;
+
+ test LQOSTAT2, LQOPHACHGOUTPKT jnz unexpected_nonpkt_phase;
+ test LQOSTAT1, LQOPHACHGINPKT jnz unexpected_nonpkt_phase;
+
+ /*
+ * If this is a packetized connection, return to our
+ * idle_loop and let our interrupt handler deal with
+ * any connection setup/teardown issues. The only
+ * exception is the case of MK_MESSAGE SCBs. In the
+ * A, the LQO manager transitions to LQOSTOP0 even if
+ * we have selected out with ATN asserted and the target
+ * REQs in a non-packet phase.
+ */
+ if ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0) {
+ test SCB_CONTROL, MK_MESSAGE jz select_out_no_message;
+ test SCSISIGO, ATNO jnz select_out_non_packetized;
+select_out_no_message:
+ }
+ test LQOSTAT2, LQOSTOP0 jnz idle_loop;
+
+select_out_non_packetized:
+ /* Non packetized request. */
+ and SCSISEQ0, ~ENSELO;
+ mov SAVED_SCSIID, SCB_SCSIID;
+ mov SAVED_LUN, SCB_LUN;
+ or SXFRCTL0, SPIOEN;
+
+ /*
+ * As soon as we get a successful selection, the target
+ * should go into the message out phase since we have ATN
+ * asserted.
+ */
+ mvi MSG_OUT, MSG_IDENTIFYFLAG;
+ mvi SEQ_FLAGS, NO_CDB_SENT;
+
+ /*
+ * Main loop for information transfer phases. Wait for the
+ * target to assert REQ before checking MSG, C/D and I/O for
+ * the bus phase.
+ */
+mesgin_phasemis:
+ITloop:
+ call phase_lock;
+
+ mov A, LASTPHASE;
+
+ test A, ~P_DATAIN_DT jz p_data;
+ cmp A,P_COMMAND je p_command;
+ cmp A,P_MESGOUT je p_mesgout;
+ cmp A,P_STATUS je p_status;
+ cmp A,P_MESGIN je p_mesgin;
+
+ mvi SEQINTCODE, BAD_PHASE;
+ jmp ITloop; /* Try reading the bus again. */
+
+/*
+ * Command phase. Set up the DMA registers and let 'er rip.
+ */
+p_command:
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+ jnz p_command_allocate_fifo;
+ /*
+ * Command retry. Free our current FIFO and
+ * re-allocate a FIFO so transfer state is
+ * reset.
+ */
+ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT;
+p_command_allocate_fifo:
+ call allocate_fifo;
+ add NONE, -17, SCB_CDB_LEN;
+ jnc p_command_embedded;
+p_command_from_host:
+ bmov HADDR[0], SCB_CDB_PTR, 11;
+ mvi SG_CACHE_PRE, LAST_SEG;
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+ jmp p_command_loop;
+p_command_embedded:
+ bmov SHCNT[0], SCB_CDB_LEN, 1;
+ bmov SHCNT[1], ALLZEROS, 2;
+ bmov DFDAT, SCB_CDB_STORE, 16;
+ mvi DFCNTRL, SCSIEN;
+p_command_loop:
+ test DFCNTRL, SCSIEN jnz p_command_loop;
+ /*
+ * DMA Channel automatically disabled.
+ * Don't allow a data phase if the command
+ * was not fully transferred. Make sure that
+ * we clear the IDENTIFY SEEN flag if a retry
+ * falls short too.
+ */
+ and SEQ_FLAGS, ~NO_CDB_SENT;
+ test SSTAT2, SDONE jnz ITloop;
+ or SEQ_FLAGS, NO_CDB_SENT;
+ jmp ITloop;
+
+
+/*
+ * Status phase. Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+p_status:
+ test SEQ_FLAGS,NOT_IDENTIFIED jz p_status_okay;
+ mvi SEQINTCODE, PROTO_VIOLATION;
+ jmp mesgin_done;
+p_status_okay:
+ mov SCB_SCSI_STATUS, SCSIDAT;
+ or SCB_CONTROL, STATUS_RCVD;
+ jmp ITloop;
+
+/*
+ * Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target. The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte. This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own. If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
+ *
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the host to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ *
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+ /* Turn on ATN for the retry */
+ mvi SCSISIGO, ATNO;
+p_mesgout:
+ mov SINDEX, MSG_OUT;
+ cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+ test SCB_CONTROL,MK_MESSAGE jnz host_message_loop;
+p_mesgout_identify:
+ or SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN;
+ test SCB_CONTROL, DISCENB jnz . + 2;
+ and SINDEX, ~DISCENB;
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_NONPACKET_TAG as the tag value.
+ */
+p_mesgout_tag:
+ test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
+ mov SCSIDAT, SINDEX; /* Send the identify message */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ and SCSIDAT,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ mov SCB_NONPACKET_TAG jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
+ */
+p_mesgout_from_host:
+ cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
+ jmp host_message_loop;
+
+p_mesgout_onebyte:
+ mvi CLRSINT1, CLRATNO;
+ mov SCSIDAT, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT je p_mesgout_retry;
+
+p_mesgout_done:
+ mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
+ mov LAST_MSG, MSG_OUT;
+ mvi MSG_OUT, MSG_NOOP; /* No message left */
+ jmp ITloop;
+
+/*
+ * Message in phase. Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+ /* read the 1st message byte */
+ mvi ACCUM call inb_first;
+
+ test A,MSG_IDENTIFYFLAG jnz mesgin_identify;
+ cmp A,MSG_DISCONNECT je mesgin_disconnect;
+ cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs;
+ cmp ALLZEROS,A je mesgin_complete;
+ cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs;
+ cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_ign_wide_residue;
+ cmp A,MSG_NOOP je mesgin_done;
+
+/*
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine. To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop. Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
+ */
+host_message_loop:
+ call phase_lock; /* Benign the first time through. */
+ mvi SEQINTCODE, HOST_MSG_LOOP;
+ cmp RETURN_1, EXIT_MSG_LOOP je ITloop;
+ cmp RETURN_1, CONT_MSG_LOOP_WRITE jne . + 3;
+ mov SCSIDAT, RETURN_2;
+ jmp host_message_loop;
+ /* Must be CONT_MSG_LOOP_READ */
+ mov NONE, SCSIDAT; /* ACK Byte */
+ jmp host_message_loop;
+
+mesgin_ign_wide_residue:
+ shr NEGOADDR, 4, SAVED_SCSIID;
+ test NEGCONOPTS, WIDEXFER jz mesgin_reject;
+ /* Pull the residue byte */
+ mvi REG0 call inb_next;
+ cmp REG0, 0x01 jne mesgin_reject;
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+ test DATA_COUNT_ODD, 0x1 jz mesgin_done;
+ mvi SEQINTCODE, IGN_WIDE_RES;
+ jmp mesgin_done;
+
+mesgin_reject:
+ mvi MSG_MESSAGE_REJECT call mk_mesg;
+mesgin_done:
+ mov NONE,SCSIDAT; /*dummy read from latch to ACK*/
+ jmp ITloop;
+
+#define INDEX_DISC_LIST_SCB(target, lun) \
+ mov SCBPTR, lun; \
+ shr SCBPTR[1], 3, target
+
+#define INDEX_DISC_LIST(target, lun) \
+ INDEX_DISC_LIST_SCB(target, lun); \
+ and SINDEX, 0x7, target; \
+ shl SINDEX, 1; \
+ add SINDEX, (SCB_DISCONNECTED_LISTS & 0xFF); \
+ mvi SINDEX[1], ((SCB_DISCONNECTED_LISTS >> 8) & 0xFF)
+
+mesgin_identify:
+ /*
+ * Determine whether a target is using tagged or non-tagged
+ * transactions by first looking at the transaction stored in
+ * the per-device, disconnected array. If there is no untagged
+ * transaction for this target this must be an untagged transaction.
+ */
+ shr SINDEX, 4, SAVED_SCSIID;
+ and SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+ INDEX_DISC_LIST(SINDEX, SAVED_LUN);
+ bmov DINDEX, SINDEX, 2;
+ bmov SCBPTR, SINDIR, 2;
+ cmp SCBPTR[1], SCB_LIST_NULL je snoop_tag;
+ test SCB_CONTROL, TAG_ENB jnz snoop_tag;
+ /* Untagged. Setup the SCB. */
+ bmov REG1, SCB_TAG, 4; /* Save SCB_TAG and SCB_NEXT */
+ jmp dequeue_first_scb;
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB. The disconnected list contains any outstanding tagged transactions
+ * where SCB_TAG != SCB_NONPACKET_TAG or SCB_NONPACKET_TAG + 256.
+ * After receiving the tag, look for the SCB at SCB locations tag and
+ * tag + 256. If those SCBs do not match, traverse the disconnected
+ * list until we find the correct SCB.
+ */
+snoop_tag:
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x80;
+ }
+ mov NONE, SCSIDAT; /* ACK Identify MSG */
+ call phase_lock;
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x1;
+ }
+ cmp LASTPHASE, P_MESGIN jne not_found_ITloop;
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x2;
+ }
+ cmp SCSIBUS, MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+ /* Save diconnected list head. */
+ bmov REG0, SCBPTR, 2;
+ clr SCBPTR[1];
+ mvi SCBPTR call inb_next; /* tag value */
+ mov REG1, SCBPTR;
+verify_scb:
+ mov A, REG1;
+ cmp SCB_NONPACKET_TAG, A jne verify_other_scb;
+ mov A, SAVED_SCSIID;
+ cmp SCB_SCSIID, A jne verify_other_scb;
+ mov A, SAVED_LUN;
+ cmp SCB_LUN, A je setup_SCB;
+verify_other_scb:
+ xor SCBPTR[1], 1;
+ test SCBPTR[1], 0xFF jnz verify_scb;
+
+search_disc_list:
+ /* Restore disconnected list head. */
+ bmov SCBPTR, REG0, 2;
+ mvi REG0[1], SCB_LIST_NULL;
+ mov A, REG1;
+search_disc_list_loop:
+ cmp SCB_NONPACKET_TAG, A je dequeue_scb;
+ bmov REG0, SCBPTR, 2;
+ bmov SCBPTR, SCB_NEXT, 2;
+ cmp SCBPTR[1], SCB_LIST_NULL jne search_disc_list_loop;
+ jmp not_found;
+
+dequeue_scb:
+ bmov REG1, SCB_TAG, 4; /* Save SCB_TAG and SCB_NEXT */
+ cmp REG0[1], SCB_LIST_NULL jne dequeue_intermediate_SCB;
+dequeue_first_scb:
+ shr SINDEX, 4, SAVED_SCSIID;
+ INDEX_DISC_LIST_SCB(SINDEX, SAVED_LUN);
+ /* Update list head. */
+ bmov DINDIR, REG2, 2;
+ jmp dequeue_restore;
+dequeue_intermediate_SCB:
+ bmov SCBPTR, REG0, 2;
+ bmov SCB_NEXT, REG2, 2;
+dequeue_restore:
+ bmov SCBPTR, REG1, 2;
+
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+setup_SCB:
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x10;
+ }
+ test SCB_CONTROL,DISCONNECTED jz not_found;
+ and SCB_CONTROL,~DISCONNECTED;
+ clr SEQ_FLAGS; /* make note of IDENTIFY */
+ test SCB_SGPTR, SG_LIST_NULL jnz . + 2;
+ call allocate_fifo;
+/* mvi SEQINTCODE, PRINT_RESIDUALS; */
+ /* See if the host wants to send a message upon reconnection */
+ test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+ mvi HOST_MSG call mk_mesg;
+ jmp mesgin_done;
+
+not_found:
+ mvi SEQINTCODE, NO_MATCH;
+ jmp mesgin_done;
+
+not_found_ITloop:
+ mvi SEQINTCODE, NO_MATCH;
+ jmp ITloop;
+
+/*
+ * We received a "command complete" message. Put the SCB on the complete
+ * queue and trigger a completion interrupt via the idle loop. Before doing
+ * so, check to see if there
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
+ * process this information. In the case of a non zero status byte, we
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved. If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command, requeue
+ * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
+ * RETURN_1 to SEND_SENSE.
+ */
+mesgin_complete:
+
+ /*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte.
+ * Either way, the target should take us to message out phase
+ * and then attempt to complete the command again. We should use a
+ * critical section here to guard against a timeout triggering
+ * for this command and setting ATN while we are still processing
+ * the completion.
+ test SCSISIGI, ATNI jnz mesgin_done;
+ */
+
+ /*
+ * If the target never sent an identify message but instead went
+ * to mesgin to give an invalid message, let the host abort us.
+ */
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz . + 3;
+ mvi SEQINTCODE, PROTO_VIOLATION;
+ jmp mesgin_done;
+
+ /*
+ * If the target never gave us status information, have
+ * the host abort the command.
+ */
+ test SCB_CONTROL, STATUS_RCVD jz . - 2;
+
+ /*
+ * See if we attempted to deliver a message but the target ingnored us.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz . + 2;
+ mvi SEQINTCODE, MKMSG_FAILED;
+ call queue_scb_completion;
+ jmp await_busfree;
+
+freeze_queue:
+ /* Cancel any pending select-out. */
+ test SSTAT0, SELDO jnz . + 2;
+ and SCSISEQ0, ~ENSELO;
+ mov ACCUM_SAVE, A;
+ clr A;
+ add QFREEZE_COUNT, 1;
+ adc QFREEZE_COUNT[1], A;
+ or SEQ_FLAGS2, SELECTOUT_QFROZEN;
+ mov A, ACCUM_SAVE ret;
+
+queue_arg1_scb_completion:
+ SET_MODE(M_SCSI, M_SCSI);
+ bmov SCBPTR, ARG_1, 2;
+queue_scb_completion:
+ test SCB_SCSI_STATUS,0xff jnz bad_status;
+ /*
+ * Check for residuals
+ */
+ test SCB_SGPTR, SG_LIST_NULL jnz complete; /* No xfer */
+ test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
+ test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
+complete:
+ bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+ bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
+bad_status:
+ cmp SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb;
+ call freeze_queue;
+upload_scb:
+ bmov SCB_NEXT_COMPLETE, COMPLETE_DMA_SCB_HEAD, 2;
+ bmov COMPLETE_DMA_SCB_HEAD, SCBPTR, 2;
+ or SCB_SGPTR, SG_STATUS_VALID ret;
+
+/*
+ * Is it a disconnect message? Set a flag in the SCB to remind us
+ * and await the bus going free. If this is an untagged transaction
+ * store the SCB id for it in our untagged target table for lookup on
+ * a reselction.
+ */
+mesgin_disconnect:
+ /*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte
+ * or we want to abort this command. Either way, the target
+ * should take us to message out phase and then attempt to
+ * disconnect again.
+ * XXX - Wait for more testing.
+ test SCSISIGI, ATNI jnz mesgin_done;
+ */
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz disconnect_allowed;
+ mvi SEQINTCODE, PROTO_VIOLATION;
+ jmp mesgin_done;
+disconnect_allowed:
+ or SCB_CONTROL,DISCONNECTED;
+ test SCB_CONTROL, TAG_ENB jz queue_disc_scb;
+ mov A, SCB_NONPACKET_TAG;
+ cmp SCBPTR, A je await_busfree;
+queue_disc_scb:
+ bmov REG0, SCBPTR, 2;
+ shr SINDEX, 4, SCB_SCSIID;
+ INDEX_DISC_LIST(SINDEX, SCB_LUN);
+ bmov DINDEX, SINDEX, 2;
+ bmov REG1, SINDIR, 2;
+ bmov DINDIR, REG0, 2;
+ bmov SCBPTR, REG0, 2;
+ bmov SCB_NEXT, REG1, 2;
+ /* FALLTHROUGH */
+await_busfree:
+ and SIMODE1, ~ENBUSFREE;
+ mov NONE, SCSIDAT; /* Ack the last byte */
+ call clear_target_state;
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+ jnz await_busfree_not_m_dff;
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+await_busfree_clrchn:
+ mvi DFFSXFRCTL, CLRCHN;
+await_busfree_not_m_dff:
+ test SSTAT1,REQINIT|BUSFREE jz .;
+ test SSTAT1, BUSFREE jnz idle_loop;
+ mvi SEQINTCODE, MISSED_BUSFREE;
+
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them. This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ * Ack the message as soon as possible. For chips without S/G pipelining,
+ * we can only ack the message after SHADDR has been saved. On these
+ * chips, SHADDR increments with every bus transaction, even PIO.
+ */
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+mesgin_sdptrs:
+ mov NONE,SCSIDAT; /*dummy read from latch to ACK*/
+ test SEQ_FLAGS, DPHASE jz ITloop;
+ call save_pointers;
+ jmp ITloop;
+
+save_pointers:
+ /*
+ * If we are asked to save our position at the end of the
+ * transfer, just mark us at the end rather than perform a
+ * full save.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz save_pointers_full;
+ or SCB_SGPTR, SG_LIST_NULL ret;
+
+save_pointers_full:
+ /*
+ * The SCB_DATAPTR becomes the current SHADDR.
+ * All other information comes directly from our residual
+ * state.
+ */
+ bmov SCB_DATAPTR, SHADDR, 8;
+ bmov SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8 ret;
+
+/*
+ * Restore pointers message? Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest. We also reset/reallocate the FIFO to make
+ * sure we have a clean start for the next data phase.
+ */
+mesgin_rdptrs:
+ and SEQ_FLAGS, ~DPHASE;
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz msgin_rdptrs_get_fifo;
+ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT;
+msgin_rdptrs_get_fifo:
+ call allocate_fifo;
+ jmp mesgin_done;
+
+clear_target_state:
+ mvi LASTPHASE, P_BUSFREE;
+ /* clear target specific flags */
+ mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;
+
+phase_lock:
+ test SCSIPHASE, 0xFF jz .;
+phase_lock_latch_phase:
+ and LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * An ACK is not sent on input from the target until SCSIDATL is read from.
+ * So we wait until SCSIDATL is latched (the usual way), then read the data
+ * byte directly off the bus using SCSIBUSL. When we have pulled the ATN
+ * line, or we just want to acknowledge the byte, then we do a dummy read
+ * from SCISDATL. The SCSI spec guarantees that the target will hold the
+ * data byte on the bus until we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called. inb_{first,next}
+ * use the same calling convention as inb.
+ */
+inb_next:
+ mov NONE,SCSIDAT; /*dummy read from latch to ACK*/
+inb_next_wait:
+ /*
+ * If there is a parity error, wait for the kernel to
+ * see the interrupt and prepare our message response
+ * before continuing.
+ */
+ test SCSIPHASE, 0xFF jz .;
+inb_next_check_phase:
+ and LASTPHASE, PHASE_MASK, SCSISIGI;
+ cmp LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+ clr DINDEX[1];
+ mov DINDEX,SINDEX;
+ mov DINDIR,SCSIBUS ret; /*read byte directly from bus*/
+inb_last:
+ mov NONE,SCSIDAT ret; /*dummy read from latch to ACK*/
+
+mk_mesg:
+ mvi SCSISIGO, ATNO;
+ mov MSG_OUT,SINDEX ret;
+
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+disable_ccsgen:
+ test SG_STATE, FETCH_INPROG jz return;
+ clr SG_STATE;
+disable_ccsgen_fetch_done:
+ clr CCSGCTL ret;
+
+toggle_dff_mode:
+ mvi SEQINTCTL, INTVEC1DSL;
+ xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+ clr SEQINTCTL ret;
+
+data_group_idle_loop:
+ mov SAVED_MODE, MODE_PTR;
+ test SG_STATE, LOADING_NEEDED jz . + 2;
+ call service_fifo;
+ call toggle_dff_mode;
+ test SG_STATE, LOADING_NEEDED jz . + 2;
+ call service_fifo;
+ call idle_loop_cchan;
+ mov SAVED_MODE jmp set_mode_work_around;
+
+service_fifo:
+ /*
+ * Do we have any prefetch left???
+ */
+ test SG_STATE, SEGS_AVAIL jnz idle_sg_avail;
+
+ /*
+ * Can this FIFO have access to the S/G cache yet?
+ */
+ test CCSGCTL, SG_CACHE_AVAIL jz return;
+
+ /* Did we just finish fetching segs? */
+ cmp CCSGCTL, CCSGEN|SG_CACHE_AVAIL|CCSGDONE
+ je idle_sgfetch_complete;
+
+ /* Are we actively fetching segments? */
+ test CCSGCTL, CCSGEN jnz return;
+
+ /*
+ * We fetch a "cacheline aligned" and sized amount of data
+ * so we don't end up referencing a non-existant page.
+ * Cacheline aligned is in quotes because the kernel will
+ * set the prefetch amount to a reasonable level if the
+ * cacheline size is unknown.
+ */
+ mvi SGHCNT, SG_PREFETCH_CNT;
+ and SGHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+ bmov SGHADDR[1], SCB_RESIDUAL_SGPTR[1], 3;
+ mvi CCSGCTL, CCSGEN|SG_CACHE_AVAIL|CCSGRESET;
+ or SG_STATE, FETCH_INPROG ret;
+idle_sgfetch_complete:
+ /*
+ * Guard against SG_CACHE_AVAIL activating during sg fetch
+ * request in the other FIFO.
+ */
+ test SG_STATE, FETCH_INPROG jz return;
+ call disable_ccsgen_fetch_done;
+ and CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
+ mvi SG_STATE, SEGS_AVAIL|LOADING_NEEDED;
+idle_sg_avail:
+ /* Does the hardware have space for another SG entry? */
+ test DFSTATUS, PRELOAD_AVAIL jz return;
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ bmov HADDR, CCSGRAM, 8;
+ } else {
+ bmov HADDR, CCSGRAM, 4;
+ }
+ bmov HCNT, CCSGRAM, 3;
+ test HCNT[0], 0x1 jz . + 2;
+ xor DATA_COUNT_ODD, 0x1;
+ bmov SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
+ if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+ and HADDR[4], SG_HIGH_ADDR_BITS, SCB_RESIDUAL_DATACNT[3];
+ }
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ /* Skip 4 bytes of pad. */
+ add CCSGADDR, 4;
+ }
+sg_advance:
+ clr A; /* add sizeof(struct scatter) */
+ add SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
+ adc SCB_RESIDUAL_SGPTR[1],A;
+ adc SCB_RESIDUAL_SGPTR[2],A;
+ adc SCB_RESIDUAL_SGPTR[3],A;
+ mov SINDEX, SCB_RESIDUAL_SGPTR[0];
+ test DATA_COUNT_ODD, 0x1 jz . + 2;
+ or SINDEX, ODD_SEG;
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 3;
+ or SINDEX, LAST_SEG;
+ clr SG_STATE;
+ mov SG_CACHE_PRE, SINDEX;
+ /*
+ * Load the segment. Or in HDMAEN here too
+ * just in case HDMAENACK has not come true
+ * by the time this segment is loaded. If
+ * HDMAENACK is not true, this or will disable
+ * HDMAEN mid-transfer. We do not want to simply
+ * mvi our original settings as SCSIEN automatically
+ * de-asserts and we don't want to accidentally
+ * re-enable it.
+ */
+ or DFCNTRL, PRELOADEN|HDMAEN;
+ /*
+ * Do we have another segment in the cache?
+ */
+ add NONE, SG_PREFETCH_CNT_LIMIT, CCSGADDR;
+ jnc return;
+ and SG_STATE, ~SEGS_AVAIL ret;
+
+/*
+ * Initialize the DMA address and counter from the SCB.
+ */
+load_first_seg:
+ bmov HADDR, SCB_DATAPTR, 11;
+ and DATA_COUNT_ODD, 0x1, SCB_DATACNT[0];
+ and REG0, ~SG_FULL_RESID, SCB_SGPTR[0];
+ test SCB_DATACNT[3], SG_LAST_SEG jz . + 2;
+ or REG0, LAST_SEG;
+ test DATA_COUNT_ODD, 0x1 jz . + 2;
+ or REG0, ODD_SEG;
+ mov SG_CACHE_PRE, REG0;
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+ /*
+ * Since we've are entering a data phase, we will
+ * rely on the SCB_RESID* fields. Initialize the
+ * residual and clear the full residual flag.
+ */
+ and SCB_SGPTR[0], ~SG_FULL_RESID;
+ bmov SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
+ /* If we need more S/G elements, tell the idle loop */
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz . + 2;
+ mvi SG_STATE, LOADING_NEEDED ret;
+ clr SG_STATE ret;
+
+p_data:
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed;
+ mvi SEQINTCODE, PROTO_VIOLATION;
+p_data_allowed:
+
+ test SEQ_FLAGS, DPHASE jz data_phase_initialize;
+
+ /*
+ * If we re-enter the data phase after going through another
+ * phase, our transfer location has almost certainly been
+ * corrupted by the interveining, non-data, transfers. Ask
+ * the host driver to fix us up based on the transfer residual
+ * unless we already know that we should be bitbucketing.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket;
+ mvi SEQINTCODE, PDATA_REINIT;
+
+p_data_bitbucket:
+ /*
+ * Turn on `Bit Bucket' mode, wait until the target takes
+ * us to another phase, and then notify the host.
+ */
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+ jnz bitbucket_not_m_dff;
+ /*
+ * Ensure that any FIFO contents are cleared out and the
+ * FIFO free'd prior to starting the BITBUCKET. BITBUCKET
+ * doesn't discard data already in the FIFO.
+ */
+ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT;
+ SET_MODE(M_SCSI, M_SCSI);
+bitbucket_not_m_dff:
+ or SXFRCTL1,BITBUCKET;
+ test SCSIPHASE, DATA_PHASE_MASK jnz .;
+ and SXFRCTL1, ~BITBUCKET;
+ SET_MODE(M_DFF1, M_DFF1);
+ mvi SEQINTCODE, DATA_OVERRUN;
+ jmp ITloop;
+
+data_phase_initialize:
+ test SCB_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket;
+ call load_first_seg;
+data_phase_inbounds:
+ /* We have seen a data phase at least once. */
+ or SEQ_FLAGS, DPHASE;
+data_group_dma_loop:
+ /*
+ * The transfer is complete if either the last segment
+ * completes or the target changes phase. Both conditions
+ * will clear SCSIEN. We test SCSIEN twice during our
+ * "idle loop" to avoid long delays before we notice the
+ * SCSIEN transition.
+ */
+ call data_group_idle_loop;
+ test DFCNTRL, SCSIEN jnz data_group_dma_loop;
+
+data_group_dmafinish:
+ /*
+ * The transfer has terminated either due to a phase
+ * change, and/or the completion of the last segment.
+ * We have two goals here. Do as much other work
+ * as possible while the data fifo drains on a read
+ * and respond as quickly as possible to the standard
+ * messages (save data pointers/disconnect and command
+ * complete) that usually follow a data phase.
+ */
+ call calc_residual;
+
+ /*
+ * Go ahead and shut down the DMA engine now.
+ */
+ test DFCNTRL, DIRECTION jnz data_phase_finish;
+data_group_fifoflush:
+ if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ /*
+ * We have enabled the auto-ack feature. This means
+ * that the controller may have already transferred
+ * some overrun bytes into the data FIFO and acked them
+ * on the bus. The only way to detect this situation is
+ * to wait for LAST_SEG_DONE to come true on a completed
+ * transfer and then test to see if the data FIFO is
+ * non-empty. We know there is more data yet to transfer
+ * if SG_LIST_NULL is not yet set, thus there cannot be
+ * an overrun.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_finish;
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jz .;
+ test DFSTATUS, FIFOEMP jnz data_phase_finish;
+ /* Overrun */
+ jmp p_data;
+data_phase_finish:
+ /*
+ * If the target has left us in data phase, loop through
+ * the dma code again. We will only loop if there is a
+ * data overrun.
+ */
+ if ((ahd->flags & AHD_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jnz data_phase_done;
+ }
+ if ((ahd->flags & AHD_INITIATORROLE) != 0) {
+ test SSTAT1, REQINIT jz .;
+ test SCSIPHASE, DATA_PHASE_MASK jnz p_data;
+ }
+
+data_phase_done:
+ /* Kill off any pending prefetch */
+ call disable_ccsgen;
+
+ if ((ahd->flags & AHD_TARGETROLE) != 0) {
+ test SEQ_FLAGS, DPHASE_PENDING jz ITloop;
+ /*
+ and SEQ_FLAGS, ~DPHASE_PENDING;
+ * For data-in phases, wait for any pending acks from the
+ * initiator before changing phase. We only need to
+ * send Ignore Wide Residue messages for data-in phases.
+ test DFCNTRL, DIRECTION jz target_ITloop;
+ test SSTAT1, REQINIT jnz .;
+ test DATA_COUNT_ODD, 0x1 jz target_ITloop;
+ SET_MODE(M_SCSI, M_SCSI);
+ test NEGCONOPTS, WIDEXFER jz target_ITloop;
+ */
+ /*
+ * Issue an Ignore Wide Residue Message.
+ mvi P_MESGIN|BSYO call change_phase;
+ mvi MSG_IGN_WIDE_RESIDUE call target_outb;
+ mvi 1 call target_outb;
+ jmp target_ITloop;
+ */
+ } else {
+ jmp ITloop;
+ }
+
+/*
+ * We assume that, even though data may still be
+ * transferring to the host, that the SCSI side of
+ * the DMA engine is now in a static state. This
+ * allows us to update our notion of where we are
+ * in this transfer.
+ *
+ * If, by chance, we stopped before being able
+ * to fetch additional segments for this transfer,
+ * yet the last S/G was completely exhausted,
+ * call our idle loop until it is able to load
+ * another segment. This will allow us to immediately
+ * pickup on the next segment on the next data phase.
+ *
+ * If we happened to stop on the last segment, then
+ * our residual information is still correct from
+ * the idle loop and there is no need to perform
+ * any fixups.
+ */
+calc_residual:
+ test SG_CACHE_SHADOW, LAST_SEG jz residual_before_last_seg;
+ /* Record if we've consumed all S/G entries */
+ test MDFFSTAT, SHVALID jz . + 2;
+ bmov SCB_RESIDUAL_DATACNT, SHCNT, 3 ret;
+ or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL ret;
+residual_before_last_seg:
+ test MDFFSTAT, SHVALID jnz sgptr_fixup;
+ /*
+ * Can never happen from an interrupt as the packetized
+ * hardware will only interrupt us once SHVALID or
+ * LAST_SEG_DONE.
+ */
+ call data_group_idle_loop;
+ jmp calc_residual;
+
+sgptr_fixup:
+ /*
+ * Fixup the residual next S/G pointer. The S/G preload
+ * feature of the chip allows us to load two elements
+ * in addition to the currently active element. We
+ * store the bottom byte of the next S/G pointer in
+ * the SG_CACHE_PTR register so we can restore the
+ * correct value when the DMA completes. If the next
+ * sg ptr value has advanced to the point where higher
+ * bytes in the address have been affected, fix them
+ * too.
+ */
+ test SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
+ test SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
+ add SCB_RESIDUAL_SGPTR[1], -1;
+ adc SCB_RESIDUAL_SGPTR[2], -1;
+ adc SCB_RESIDUAL_SGPTR[3], -1;
+sgptr_fixup_done:
+ and SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
+ clr DATA_COUNT_ODD;
+ test SG_CACHE_SHADOW, ODD_SEG jz . + 2;
+ or DATA_COUNT_ODD, 0x1;
+ clr SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */
+ bmov SCB_RESIDUAL_DATACNT, SHCNT, 3 ret;
+
+export seq_isr:
+ nop; /* Jumps in the first ISR instruction fail on Rev A. */
+ test SEQINTSRC, SAVEPTRS jnz saveptr_intr;
+ test SEQINTSRC, CFG4DATA jnz cfg4data_intr;
+ test SEQINTSRC, CFG4ISTAT jnz cfg4istat_intr;
+ test SEQINTSRC, CFG4ICMD jnz cfg4icmd_intr;
+ mvi SEQINTCODE, INVALID_SEQINT;
+
+/*
+ * There are two types of save pointers interrupts:
+ * The first is a snapshot save pointers where the current FIFO is not
+ * active and contains a snapshot of the current poniter information.
+ * This happens between packets in a stream for a single L_Q. Since we
+ * are not performing a pointer save, we can safely clear the channel
+ * so it can be used for other transactions.
+ *
+ * The second case is a save pointers on an active FIFO which occurs
+ * if the target changes to a new L_Q or busfrees/QAS' and the transfer
+ * has a residual. This should occur coincident with a ctxtdone. We
+ * disable the interrupt and allow our active routine to handle the
+ * save.
+ */
+saveptr_intr:
+ test DFCNTRL, HDMAENACK jz snapshot_saveptr;
+ and SEQIMODE, ~ENSAVEPTRS;
+ or SEQINTCTL, IRET ret;
+snapshot_saveptr:
+ mvi DFFSXFRCTL, CLRCHN;
+ or SEQINTCTL, IRET ret;
+
+cfg4data_intr:
+ test SCB_SGPTR[0], SG_LIST_NULL jnz pkt_handle_overrun;
+ call load_first_seg;
+ call pkt_handle_xfer;
+ or SEQINTCTL, IRET ret;
+
+cfg4istat_intr:
+ call freeze_queue;
+ add NONE, -13, SCB_CDB_LEN;
+ jnc cfg4istat_have_sense_addr;
+ test SCB_CDB_LEN, SCB_CDB_LEN_PTR jnz cfg4istat_have_sense_addr;
+ /*
+ * Host sets up address/count and enables transfer.
+ */
+ mvi SEQINTCODE, CFG4ISTAT_INTR;
+ jmp cfg4istat_setup_handler;
+cfg4istat_have_sense_addr:
+ bmov HADDR, SCB_SENSE_BUSADDR, 4;
+ mvi HCNT[1], (AHD_SENSE_BUFSIZE >> 8);
+ mvi SG_CACHE_PRE, LAST_SEG;
+ mvi DFCNTRL, PRELOADEN|SCSIEN|HDMAEN;
+cfg4istat_setup_handler:
+ /*
+ * Status pkt is transferring to host.
+ * Wait in idle loop for transfer to complete.
+ */
+ call pkt_handle_status;
+ or SEQINTCTL, IRET ret;
+
+/*
+ * See if the target has gone on in this context creating an
+ * overrun condition. For the write case, the hardware cannot
+ * ack bytes until data is provided. So, if the target begins
+ * another packet without changing contexts, implying we are
+ * not sitting on a packet boundary, we are in an overrun
+ * situation. For the read case, the hardware will continue to
+ * ack bytes into the FIFO, and may even ack the last overrun packet
+ * into the FIFO. If the FIFO should become non-empty, we are in
+ * a read overrun case.
+ */
+#define check_overrun \
+ /* Not on a packet boundary. */ \
+ test MDFFSTAT, DLZERO jz pkt_handle_overrun; \
+ test DFSTATUS, FIFOEMP jz pkt_handle_overrun
+
+pkt_handle_xfer:
+ bmov LONGJMP_SCB, SCBPTR, 2;
+ test SG_STATE, LOADING_NEEDED jz pkt_last_seg;
+ call setjmp;
+ test SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
+ test SCSIPHASE, ~DATA_PHASE_MASK jz . + 2;
+ test SCSISIGO, ATNO jnz . + 2;
+ test SSTAT2, NONPACKREQ jz pkt_service_fifo;
+ /*
+ * Defer handling of this NONPACKREQ until we
+ * can be sure it pertains to this FIFO. SAVEPTRS
+ * will not be asserted if the NONPACKREQ is for us,
+ * so we must simulate it if shaddow is valid. If
+ * shaddow is not valid, keep running this FIFO until we
+ * have satisfied the transfer by loading segments and
+ * waiting for either shaddow valid or last_seg_done.
+ */
+ test MDFFSTAT, SHVALID jnz pkt_saveptrs;
+pkt_service_fifo:
+ test SG_STATE, LOADING_NEEDED jnz service_fifo;
+pkt_last_seg:
+ call setjmp;
+ test SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jnz last_pkt_done;
+ test SCSIPHASE, ~DATA_PHASE_MASK jz . + 2;
+ test SCSISIGO, ATNO jnz . + 2;
+ test SSTAT2, NONPACKREQ jz return;
+ test MDFFSTAT, SHVALID jnz pkt_saveptrs;
+ jmp return;
+last_pkt_done:
+BEGIN_CRITICAL;
+ if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ test SCB_CONTROL, STATUS_RCVD jz wait_pkt_end;
+ check_overrun;
+ or SCB_SGPTR, SG_LIST_NULL;
+ /*
+ * I think it is safe to skip the FIFO check.
+ * in this case as LAST_SEG_DONE implies
+ * the other FIFO, if ever active for this transfer,
+ * has completed.
+ */
+last_pkt_queue_scb:
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ bmov ARG_1, SCBPTR, 2;
+ mvi DFFSXFRCTL, CLRCHN;
+ jmp queue_arg1_scb_completion;
+
+last_pkt_complete:
+ bmov ARG_1, SCBPTR, 2;
+ mvi DFFSXFRCTL, CLRCHN;
+check_other_fifo:
+ clc;
+ call toggle_dff_mode;
+ call check_fifo;
+ jnc queue_arg1_scb_completion;
+return:
+ ret;
+
+wait_pkt_end:
+ call setjmp;
+END_CRITICAL;
+wait_pkt_end_loop:
+ test SEQINTSRC, CTXTDONE jnz pkt_end;
+ check_overrun;
+ test SSTAT2, NONPACKREQ jz return;
+ test SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
+pkt_end:
+BEGIN_CRITICAL;
+ check_overrun;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ or SCB_SGPTR, SG_LIST_NULL;
+ test SCB_CONTROL, STATUS_RCVD jnz last_pkt_complete;
+ mvi DFFSXFRCTL, CLRCHN ret;
+END_CRITICAL;
+
+/*
+ * Either a SAVEPTRS interrupt condition is pending for this FIFO
+ * or we have a pending nonpackreq for this FIFO. We differentiate
+ * between the two by capturing the state of the SAVEPTRS interrupt
+ * prior to clearing and handling the common code of these two cases.
+ */
+pkt_saveptrs:
+BEGIN_CRITICAL;
+ if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ mov REG0, SEQINTSRC;
+ mvi CLRSEQINTSRC, CLRSAVEPTRS;
+ call calc_residual;
+ call save_pointers;
+ call disable_ccsgen;
+ or SEQIMODE, ENSAVEPTRS;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+pkt_saveptrs_check_status:
+ test REG0, SAVEPTRS jz unexpected_nonpkt_phase;
+ test SCB_CONTROL, STATUS_RCVD jz pkt_saveptrs_clrchn;
+ jmp last_pkt_complete;
+pkt_saveptrs_clrchn:
+ mvi DFFSXFRCTL, CLRCHN ret;
+END_CRITICAL;
+
+check_status_overrun:
+ test SHCNT[2], 0xFF jz status_IU_done;
+ mvi SEQINTCODE, STATUS_OVERRUN;
+ jmp status_IU_done;
+pkt_handle_status:
+ call setjmp_setscb;
+ test MDFFSTAT, LASTSDONE jnz check_status_overrun;
+ test SEQINTSRC, CTXTDONE jz return;
+status_IU_done:
+BEGIN_CRITICAL;
+ if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ mvi SCB_SCSI_STATUS, STATUS_PKT_SENSE;
+ or SCB_CONTROL, STATUS_RCVD;
+ jmp last_pkt_complete;
+END_CRITICAL;
+
+SET_SRC_MODE M_DFF0;
+SET_DST_MODE M_DFF0;
+BEGIN_CRITICAL;
+check_fifo:
+ test LONGJMP_ADDR[1], INVALID_ADDR jnz return;
+ mov A, ARG_2;
+ cmp LONGJMP_SCB[1], A jne return;
+ mov A, ARG_1;
+ cmp LONGJMP_SCB[0], A jne return;
+ stc ret;
+END_CRITICAL;
+
+/*
+ * Nonpackreq is a polled status. It can come true in three situations:
+ * we have received an L_Q, we have sent one or more L_Qs, or there is no
+ * L_Q context associated with this REQ (REQ occurs immediately after a
+ * (re)selection). Routines that know that the context responsible for this
+ * nonpackreq call directly into unexpected_nonpkt_phase. In the case of the
+ * top level idle loop, we exhaust all active contexts prior to determining that
+ * we simply do not have the full I_T_L_Q for this phase.
+ */
+unexpected_nonpkt_phase_find_ctxt:
+ /*
+ * This nonpackreq is most likely associated with one of the tags
+ * in a FIFO or an outgoing LQ. Only treat it as an I_T only
+ * nonpackreq if we've cleared out the FIFOs and handled any
+ * pending SELDO.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+ and A, FIFO1FREE|FIFO0FREE, DFFSTAT;
+ cmp A, FIFO1FREE|FIFO0FREE jne return;
+ test SSTAT0, SELDO jnz return;
+ mvi SCBPTR[1], SCB_LIST_NULL;
+unexpected_nonpkt_phase:
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz . + 3;
+SET_SRC_MODE M_DFF0;
+SET_DST_MODE M_DFF0;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ mvi DFFSXFRCTL, CLRCHN;
+ mvi CLRSINT2, CLRNONPACKREQ;
+ test SCSIPHASE, ~(MSG_IN_PHASE|MSG_OUT_PHASE) jnz illegal_phase;
+ mvi SEQINTCODE, ENTERING_NONPACK;
+ jmp ITloop;
+
+illegal_phase:
+ mvi SEQINTCODE, ILLEGAL_PHASE;
+ jmp ITloop;
+
+/*
+ * We have entered an overrun situation. If we have working
+ * BITBUCKET, flip that on and let the hardware eat any overrun
+ * data. Otherwise use an overrun buffer in the host to simulate
+ * BITBUCKET.
+ */
+pkt_handle_overrun:
+ mvi SEQINTCODE, CFG4OVERRUN;
+ call freeze_queue;
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0) {
+ SET_MODE(M_SCSI, M_SCSI);
+ or SXFRCTL1,BITBUCKET;
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ } else {
+ call load_overrun_buf;
+ mvi DFCNTRL, (HDMAEN|SCSIEN|PRELOADEN);
+ }
+ call setjmp;
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+ test DFSTATUS, PKT_PRELOAD_AVAIL jz overrun_load_done;
+ call load_overrun_buf;
+ or DFCNTRL, PRELOADEN;
+overrun_load_done:
+ }
+ test SEQINTSRC, CTXTDONE jnz pkt_overrun_end;
+ test SSTAT2, NONPACKREQ jz return;
+pkt_overrun_end:
+ or SCB_RESIDUAL_SGPTR, SG_OVERRUN_RESID;
+ test SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
+ test SCB_CONTROL, STATUS_RCVD jnz last_pkt_queue_scb;
+ mvi DFFSXFRCTL, CLRCHN ret;
+
+if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+load_overrun_buf:
+ /*
+ * Load a dummy segment if preload space is available.
+ */
+ mov HADDR[0], SHARED_DATA_ADDR;
+ add HADDR[1], PKT_OVERRUN_BUFOFFSET, SHARED_DATA_ADDR[1];
+ mov ACCUM_SAVE, A;
+ clr A;
+ adc HADDR[2], A, SHARED_DATA_ADDR[2];
+ adc HADDR[3], A, SHARED_DATA_ADDR[3];
+ mov A, ACCUM_SAVE;
+ bmov HADDR[4], ALLZEROS, 4;
+ /* PKT_OVERRUN_BUFSIZE is a multiple of 256 */
+ clr HCNT[0];
+ mvi HCNT[1], ((PKT_OVERRUN_BUFSIZE >> 8) & 0xFF);
+ clr HCNT[2];
+}
+
+cfg4icmd_intr:
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_core.c b/xen/drivers/scsi/aic7xxx/aic79xx_core.c
new file mode 100644
index 0000000000..660af5fe24
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -0,0 +1,8344 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#79 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+#else
+#include <dev/aic7xxx/aic79xx_osm.h>
+#include <dev/aic7xxx/aic79xx_inline.h>
+#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
+#endif
+
+/****************************** Softc Data ************************************/
+struct ahd_softc_tailq ahd_tailq = TAILQ_HEAD_INITIALIZER(ahd_tailq);
+
+/***************************** Lookup Tables **********************************/
+char *ahd_chip_names[] =
+{
+ "NONE",
+ "aic7901",
+ "aic7902"
+};
+static const u_int num_chip_names = NUM_ELEMENTS(ahd_chip_names);
+
+/*
+ * Hardware error codes.
+ */
+struct ahd_hard_error_entry {
+ uint8_t errno;
+ char *errmesg;
+};
+
+static struct ahd_hard_error_entry ahd_hard_errors[] = {
+ { DSCTMOUT, "Discard Timer has timed out" },
+ { ILLOPCODE, "Illegal Opcode in sequencer program" },
+ { SQPARERR, "Sequencer Parity Error" },
+ { DPARERR, "Data-path Parity Error" },
+ { MPARERR, "Scratch or SCB Memory Parity Error" },
+ { CIOPARERR, "CIOBUS Parity Error" },
+};
+static const u_int num_errors = NUM_ELEMENTS(ahd_hard_errors);
+
+static struct ahd_phase_table_entry ahd_phase_table[] =
+{
+ { P_DATAOUT, MSG_NOOP, "in Data-out phase" },
+ { P_DATAIN, MSG_INITIATOR_DET_ERR, "in Data-in phase" },
+ { P_DATAOUT_DT, MSG_NOOP, "in DT Data-out phase" },
+ { P_DATAIN_DT, MSG_INITIATOR_DET_ERR, "in DT Data-in phase" },
+ { P_COMMAND, MSG_NOOP, "in Command phase" },
+ { P_MESGOUT, MSG_NOOP, "in Message-out phase" },
+ { P_STATUS, MSG_INITIATOR_DET_ERR, "in Status phase" },
+ { P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
+ { P_BUSFREE, MSG_NOOP, "while idle" },
+ { 0, MSG_NOOP, "in unknown phase" }
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = NUM_ELEMENTS(ahd_phase_table) - 1;
+
+/* Our Sequencer Program */
+#include "aic79xx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static void ahd_handle_transmission_error(struct ahd_softc *ahd);
+static void ahd_handle_lqiphase_error(struct ahd_softc *ahd,
+ u_int lqistat1);
+static int ahd_handle_pkt_busfree(struct ahd_softc *ahd,
+ u_int busfreetime);
+static int ahd_handle_nonpkt_busfree(struct ahd_softc *ahd);
+static void ahd_force_renegotiation(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+
+static struct ahd_tmode_tstate*
+ ahd_alloc_tstate(struct ahd_softc *ahd,
+ u_int scsi_id, char channel);
+#ifdef AHD_TARGET_MODE
+static void ahd_free_tstate(struct ahd_softc *ahd,
+ u_int scsi_id, char channel, int force);
+#endif
+static void ahd_devlimited_syncrate(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *,
+ u_int *period,
+ u_int *ppr_options,
+ role_t role);
+static void ahd_update_neg_table(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_transinfo *tinfo);
+static void ahd_update_pending_scbs(struct ahd_softc *ahd);
+static void ahd_fetch_devinfo(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_scb_devinfo(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ struct scb *scb);
+static void ahd_setup_initiator_msgout(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ struct scb *scb);
+static void ahd_build_transfer_msg(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_construct_sdtr(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int period, u_int offset);
+static void ahd_construct_wdtr(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int bus_width);
+static void ahd_construct_ppr(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int period, u_int offset,
+ u_int bus_width, u_int ppr_options);
+static void ahd_clear_msg_state(struct ahd_softc *ahd);
+static void ahd_handle_message_phase(struct ahd_softc *ahd);
+typedef enum {
+ AHDMSG_1B,
+ AHDMSG_2B,
+ AHDMSG_EXT
+} ahd_msgtype;
+static int ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type,
+ u_int msgval, int full);
+static int ahd_parse_msg(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static int ahd_handle_msg_reject(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_handle_ign_wide_residue(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_reinitialize_dataptrs(struct ahd_softc *ahd);
+static void ahd_handle_devreset(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ cam_status status, char *message,
+ int verbose_level);
+#if AHD_TARGET_MODE
+static void ahd_setup_target_msgin(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ struct scb *scb);
+#endif
+
+static bus_size_t ahd_sglist_size(struct ahd_softc *ahd);
+static bus_size_t ahd_sglist_allocsize(struct ahd_softc *ahd);
+static bus_dmamap_callback_t
+ ahd_dmamap_cb;
+static void ahd_initialize_hscbs(struct ahd_softc *ahd);
+static int ahd_init_scbdata(struct ahd_softc *ahd);
+static void ahd_fini_scbdata(struct ahd_softc *ahd);
+static void ahd_setup_iocell_workaround(struct ahd_softc *ahd);
+static void ahd_iocell_first_selection(struct ahd_softc *ahd);
+static void ahd_chip_init(struct ahd_softc *ahd);
+static void ahd_qinfifo_requeue(struct ahd_softc *ahd,
+ struct scb *prev_scb,
+ struct scb *scb);
+static int ahd_qinfifo_count(struct ahd_softc *ahd);
+static int ahd_search_scb_list(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status,
+ ahd_search_action action,
+ u_int *list_head, u_int tid);
+static void ahd_stitch_tid_list(struct ahd_softc *ahd,
+ u_int tid_prev, u_int tid_cur,
+ u_int tid_next);
+static void ahd_add_scb_to_free_list(struct ahd_softc *ahd,
+ u_int scbid);
+static u_int ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
+ u_int prev, u_int next, u_int tid);
+static void ahd_reset_current_bus(struct ahd_softc *ahd);
+static ahd_callback_t ahd_reset_poll;
+#ifdef AHD_DUMP_SEQ
+static void ahd_dumpseq(struct ahd_softc *ahd);
+#endif
+static void ahd_loadseq(struct ahd_softc *ahd);
+static int ahd_check_patch(struct ahd_softc *ahd,
+ struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr);
+static u_int ahd_resolve_seqaddr(struct ahd_softc *ahd,
+ u_int address);
+static void ahd_download_instr(struct ahd_softc *ahd,
+ u_int instrptr, uint8_t *dconsts);
+#ifdef AHD_TARGET_MODE
+static void ahd_queue_lstate_event(struct ahd_softc *ahd,
+ struct ahd_tmode_lstate *lstate,
+ u_int initiator_id,
+ u_int event_type,
+ u_int event_arg);
+static void ahd_update_scsiid(struct ahd_softc *ahd,
+ u_int targid_mask);
+static int ahd_handle_target_cmd(struct ahd_softc *ahd,
+ struct target_cmd *cmd);
+#endif
+
+/******************************** Private Inlines *****************************/
+static __inline void ahd_assert_atn(struct ahd_softc *ahd);
+static __inline int ahd_currently_packetized(struct ahd_softc *ahd);
+static __inline int ahd_set_active_fifo(struct ahd_softc *ahd);
+
+static __inline void
+ahd_assert_atn(struct ahd_softc *ahd)
+{
+ ahd_outb(ahd, SCSISIGO, ATNO);
+}
+
+/*
+ * Determine if the current connection has a packetized
+ * agreement. This does not necessarily mean that we
+ * are currently in a packetized transfer. We could
+ * just as easily be sending or receiving a message.
+ */
+static __inline int
+ahd_currently_packetized(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ int packetized;
+
+ saved_modes = ahd_save_modes(ahd);
+ if ((ahd->bugs & AHD_PKTIZED_STATUS_BUG) != 0) {
+ /*
+ * The packetized bit refers to the last
+ * connection, not the current one. Check
+ * for non-zero LQISTATE instead.
+ */
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ packetized = ahd_inb(ahd, LQISTATE) != 0;
+ } else {
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ packetized = ahd_inb(ahd, LQISTAT2) & PACKETIZED;
+ }
+ ahd_restore_modes(ahd, saved_modes);
+ return (packetized);
+}
+
+static __inline int
+ahd_set_active_fifo(struct ahd_softc *ahd)
+{
+ u_int active_fifo;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ active_fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO;
+/* XXX This is a three possition switch in the B. */
+ switch (active_fifo) {
+ case 0:
+ case 1:
+ ahd_set_modes(ahd, active_fifo, active_fifo);
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+void
+ahd_restart(struct ahd_softc *ahd)
+{
+
+ ahd_pause(ahd);
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ /* No more pending messages */
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, SCSISIGO, 0); /* De-assert BSY */
+ ahd_outb(ahd, MSG_OUT, MSG_NOOP); /* No message to send */
+ ahd_outb(ahd, SXFRCTL1, ahd_inb(ahd, SXFRCTL1) & ~BITBUCKET);
+ ahd_outb(ahd, SEQINTCTL, 0);
+ ahd_outb(ahd, LASTPHASE, P_BUSFREE);
+ ahd_outb(ahd, SEQ_FLAGS, 0);
+ ahd_outb(ahd, SAVED_SCSIID, 0xFF);
+ ahd_outb(ahd, SAVED_LUN, 0xFF);
+
+ /*
+ * Ensure that the sequencer's idea of TQINPOS
+ * matches our own. The sequencer increments TQINPOS
+ * only after it sees a DMA complete and a reset could
+ * occur before the increment leaving the kernel to believe
+ * the command arrived but the sequencer to not.
+ */
+ ahd_outb(ahd, TQINPOS, ahd->tqinfifonext);
+
+ /* Always allow reselection */
+ ahd_outb(ahd, SCSISEQ1,
+ ahd_inb(ahd, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+ /* Ensure that no DMA operations are in progress */
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+ ahd_outb(ahd, SCBHCNT, 0);
+ ahd_outb(ahd, CCSCBCTL, CCSCBRESET);
+
+ ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
+ ahd_unpause(ahd);
+}
+
+void
+ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo)
+{
+ ahd_mode_state saved_modes;
+
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_FIFOS) != 0)
+ printf("%s: Clearing FIFO %d\n", ahd_name(ahd), fifo);
+#endif
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, fifo, fifo);
+ ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT);
+ if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
+ ahd_outb(ahd, CCSGCTL, CCSGRESET);
+ ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+ ahd_outb(ahd, SG_STATE, 0);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/************************* Input/Output Queues ********************************/
+void
+ahd_run_qoutfifo(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ u_int scb_index;
+
+ ahd_sync_qoutfifo(ahd, BUS_DMASYNC_POSTREAD);
+ while (ahd->qoutfifo[ahd->qoutfifonext] != SCB_LIST_NULL_LE) {
+
+ scb_index = ahd_le16toh(ahd->qoutfifo[ahd->qoutfifonext]);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ if (scb == NULL) {
+ printf("%s: WARNING no command for scb %d "
+ "(cmdcmplt)\nQOUTPOS = %d\n",
+ ahd_name(ahd), scb_index,
+ ahd->qoutfifonext);
+ ahd_dump_card_state(ahd);
+ ahd->qoutfifonext = AHD_QOUT_WRAP(ahd->qoutfifonext+1);
+ continue;
+ }
+
+ if ((ahd->qoutfifonext & 0x01) == 0x01) {
+
+ /*
+ * Clear 32bits of QOUTFIFO at a time
+ * so that we don't clobber an incoming
+ * 16bit DMA to the array on architectures
+ * that only support 32bit load and store
+ * operations.
+ */
+ ahd->qoutfifo[ahd->qoutfifonext - 1] = SCB_LIST_NULL_LE;
+ ahd->qoutfifo[ahd->qoutfifonext] = SCB_LIST_NULL_LE;
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap,
+ /*offset*/(ahd->qoutfifonext - 1)*2,
+ /*len*/4, BUS_DMASYNC_PREREAD);
+ }
+ ahd->qoutfifonext = AHD_QOUT_WRAP(ahd->qoutfifonext+1);
+
+ ahd_complete_scb(ahd, scb);
+ }
+}
+
+void
+ahd_run_untagged_queues(struct ahd_softc *ahd)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ ahd_run_untagged_queue(ahd, &ahd->untagged_queues[i]);
+}
+
+void
+ahd_run_untagged_queue(struct ahd_softc *ahd, struct scb_tailq *queue)
+{
+ struct scb *scb;
+
+ if (ahd->untagged_queue_lock != 0)
+ return;
+
+ if ((scb = TAILQ_FIRST(queue)) != NULL
+ && (scb->flags & SCB_ACTIVE) == 0) {
+ scb->flags |= SCB_ACTIVE;
+ ahd_queue_scb(ahd, scb);
+ }
+}
+
+/************************* Interrupt Handling *********************************/
+void
+ahd_handle_hwerrint(struct ahd_softc *ahd)
+{
+ /*
+ * Some catastrophic hardware error has occurred.
+ * Print it for the user and disable the controller.
+ */
+ int i;
+ int error;
+
+ error = ahd_inb(ahd, ERROR);
+ for (i = 0; i < num_errors; i++) {
+ if ((error & ahd_hard_errors[i].errno) != 0)
+ printf("%s: hwerrint, %s\n",
+ ahd_name(ahd), ahd_hard_errors[i].errmesg);
+ }
+
+ ahd_dump_card_state(ahd);
+ panic("BRKADRINT");
+
+ /* Tell everyone that this HBA is no longer availible */
+ ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+ CAM_NO_HBA);
+
+ /* Tell the system that this controller has gone away. */
+ ahd_free(ahd);
+}
+
+void
+ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
+{
+ u_int seqintcode;
+
+ /*
+ * Save the sequencer interrupt code and clear the SEQINT
+ * bit. We will unpause the sequencer, if appropriate,
+ * after servicing the request.
+ */
+ seqintcode = ahd_inb(ahd, SEQINTCODE);
+ ahd_outb(ahd, CLRINT, CLRSEQINT);
+ ahd_update_modes(ahd);
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: Handle Seqint Called for code %d\n",
+ ahd_name(ahd), seqintcode);
+#endif
+ switch (seqintcode) {
+ case ENTERING_NONPACK:
+ {
+ struct scb *scb;
+ u_int scbid;
+
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ /*
+ * Somehow need to know if this
+ * is from a selection or reselection.
+ * From that, we can termine target
+ * ID so we at least have an I_T nexus.
+ */
+ } else {
+ ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
+ ahd_outb(ahd, SAVED_LUN, scb->hscb->lun);
+ ahd_outb(ahd, SEQ_FLAGS, 0x0);
+ }
+ if ((ahd_inb(ahd, LQISTAT2) & LQIPHASE_OUTPKT) != 0
+ && (ahd_inb(ahd, SCSISIGO) & ATNO) != 0) {
+ /*
+ * Phase change after read stream with
+ * CRC error with P0 asserted on last
+ * packet.
+ */
+ printf("Assuming LQIPHASE_NLQ with P0 assertion\n");
+ }
+ printf("Entering NONPACK\n");
+ break;
+ }
+ case INVALID_SEQINT:
+ printf("%s: Invalid Sequencer interrupt occurred.\n",
+ ahd_name(ahd));
+ ahd_dump_card_state(ahd);
+ printf("invalid seqint");
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ break;
+ case STATUS_OVERRUN:
+ {
+ printf("%s: Status Overrun", ahd_name(ahd));
+ ahd_dump_card_state(ahd);
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ break;
+ }
+ case CFG4ISTAT_INTR:
+ {
+ struct scb *scb;
+ u_int scbid;
+
+ ahd_update_modes(ahd);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ ahd_dump_card_state(ahd);
+ printf("CFG4ISTAT: Free SCB %d referenced", scbid);
+ panic("For safety");
+ }
+ ahd_outq(ahd, HADDR, scb->sense_busaddr);
+ ahd_outw(ahd, HCNT, AHD_SENSE_BUFSIZE);
+ ahd_outb(ahd, HCNT + 2, 0);
+ ahd_outb(ahd, SG_CACHE_PRE, SG_LAST_SEG);
+ ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);
+ break;
+ }
+ case ILLEGAL_PHASE:
+ {
+ u_int bus_phase;
+
+ bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+ printf("%s: ILLEGAL_PHASE 0x%x\n",
+ ahd_name(ahd), bus_phase);
+
+ switch (bus_phase) {
+ case P_DATAOUT:
+ case P_DATAIN:
+ case P_DATAOUT_DT:
+ case P_DATAIN_DT:
+ case P_MESGOUT:
+ case P_STATUS:
+ case P_MESGIN:
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ printf("%s: Issued Bus Reset.\n", ahd_name(ahd));
+ break;
+ case P_COMMAND:
+ {
+ struct ahd_devinfo devinfo;
+ struct scb *scb;
+ struct ahd_initiator_tinfo *targ_info;
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_transinfo *tinfo;
+ u_int scbid;
+
+ /*
+ * If a target takes us into the command phase
+ * assume that it has been externally reset and
+ * has thus lost our previous packetized negotiation
+ * agreement. Since we have not sent an identify
+ * message and may not have fully qualified the
+ * connection, we change our command to TUR, assert
+ * ATN and ABORT the task when we go to message in
+ * phase. The OSM will see the REQUEUE_REQUEST
+ * status and retry the command.
+ */
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("Invalid phase with no valid SCB. "
+ "Resetting bus.\n");
+ ahd_reset_channel(ahd, 'A',
+ /*Initiate Reset*/TRUE);
+ break;
+ }
+ ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb),
+ SCB_GET_TARGET(ahd, scb),
+ SCB_GET_LUN(scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ ROLE_INITIATOR);
+ targ_info = ahd_fetch_transinfo(ahd,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo = &targ_info->curr;
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahd_set_syncrate(ahd, &devinfo, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHD_TRANS_ACTIVE, /*paused*/TRUE);
+ ahd_outb(ahd, SCB_CDB_STORE, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+1, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+2, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+3, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+4, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+5, 0);
+ ahd_outb(ahd, SCB_CDB_LEN, 6);
+ scb->hscb->control &= ~(TAG_ENB|SCB_TAG_TYPE);
+ scb->hscb->control |= MK_MESSAGE;
+ ahd_outb(ahd, SCB_CONTROL, scb->hscb->control);
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
+ /*
+ * The lun is 0, regardless of the SCB's lun
+ * as we have not sent an identify message.
+ */
+ ahd_outb(ahd, SAVED_LUN, 0);
+ ahd_outb(ahd, SEQ_FLAGS, 0);
+ ahd_assert_atn(ahd);
+ scb->flags &= ~(SCB_PACKETIZED);
+ scb->flags |= SCB_ABORT|SCB_CMDPHASE_ABORT;
+ ahd_freeze_devq(ahd, scb);
+ ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+ ahd_freeze_scb(scb);
+
+ /*
+ * Allow the sequencer to continue with
+ * non-pack processing.
+ */
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, CLRLQOINT1, CLRLQOPHACHGINPKT);
+ if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+ ahd_outb(ahd, CLRLQOINT1, 0);
+ }
+ printf("Continuing non-pack processing...\n");
+ break;
+ }
+ }
+ break;
+ }
+ case CFG4OVERRUN:
+ printf("%s: CFG4OVERRUN mode = %x\n", ahd_name(ahd),
+ ahd_inb(ahd, MODE_PTR));
+ break;
+ case DUMP_CARD_STATE:
+ {
+ ahd_dump_card_state(ahd);
+ break;
+ }
+ case PDATA_REINIT:
+ {
+ printf("%s: PDATA_REINIT - DFCNTRL = 0x%x "
+ "SG_CACHE_SHADOW = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, DFCNTRL),
+ ahd_inb(ahd, SG_CACHE_SHADOW));
+ ahd_reinitialize_dataptrs(ahd);
+ break;
+ }
+ case HOST_MSG_LOOP:
+ {
+ struct ahd_devinfo devinfo;
+
+ /*
+ * The sequencer has encountered a message phase
+ * that requires host assistance for completion.
+ * While handling the message phase(s), we will be
+ * notified by the sequencer after each byte is
+ * transfered so we can track bus phase changes.
+ *
+ * If this is the first time we've seen a HOST_MSG_LOOP
+ * interrupt, initialize the state of the host message
+ * loop.
+ */
+ ahd_fetch_devinfo(ahd, &devinfo);
+ if (ahd->msg_type == MSG_TYPE_NONE) {
+ struct scb *scb;
+ u_int scb_index;
+ u_int bus_phase;
+
+ bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+ if (bus_phase != P_MESGIN
+ && bus_phase != P_MESGOUT) {
+ printf("ahd_intr: HOST_MSG_LOOP bad "
+ "phase 0x%x\n",
+ bus_phase);
+ /*
+ * Probably transitioned to bus free before
+ * we got here. Just punt the message.
+ */
+ ahd_dump_card_state(ahd);
+ ahd_clear_intstat(ahd);
+ ahd_restart(ahd);
+ return;
+ }
+
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ if (devinfo.role == ROLE_INITIATOR) {
+ if (bus_phase == P_MESGOUT)
+ ahd_setup_initiator_msgout(ahd,
+ &devinfo,
+ scb);
+ else {
+ ahd->msg_type =
+ MSG_TYPE_INITIATOR_MSGIN;
+ ahd->msgin_index = 0;
+ }
+ }
+#if AHD_TARGET_MODE
+ else {
+ if (bus_phase == P_MESGOUT) {
+ ahd->msg_type =
+ MSG_TYPE_TARGET_MSGOUT;
+ ahd->msgin_index = 0;
+ }
+ else
+ ahd_setup_target_msgin(ahd,
+ &devinfo,
+ scb);
+ }
+#endif
+ }
+
+ ahd_handle_message_phase(ahd);
+ break;
+ }
+ case NO_MATCH:
+ {
+ /* Ensure we don't leave the selection hardware on */
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+
+ printf("%s:%c:%d: no active SCB for reconnecting "
+ "target - issuing BUS DEVICE RESET\n",
+ ahd_name(ahd), 'A', ahd_inb(ahd, SELID));
+ printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+ "ARG_1 == 0x%x ACCUM = 0x%x\n",
+ ahd_inb(ahd, SAVED_SCSIID), ahd_inb(ahd, SAVED_LUN),
+ ahd_inb(ahd, ARG_1), ahd_inb(ahd, ACCUM));
+ printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+ "SINDEX == 0x%x\n",
+ ahd_inb(ahd, SEQ_FLAGS), ahd_get_scbptr(ahd),
+ ahd_find_busy_tcl(ahd,
+ BUILD_TCL(ahd_inb(ahd, SAVED_SCSIID),
+ ahd_inb(ahd, SAVED_LUN))),
+ ahd_inb(ahd, SINDEX));
+ printf("SELID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+ "SCB_CONTROL == 0x%x\n",
+ ahd_inb(ahd, SELID), ahd_inb_scbram(ahd, SCB_SCSIID),
+ ahd_inb_scbram(ahd, SCB_LUN),
+ ahd_inb_scbram(ahd, SCB_CONTROL));
+ printf("SCSIBUS[0] == 0x%x, SCSISIGI == 0x%x\n",
+ ahd_inb(ahd, SCSIBUS), ahd_inb(ahd, SCSISIGI));
+ printf("SXFRCTL0 == 0x%x\n", ahd_inb(ahd, SXFRCTL0));
+ printf("SEQCTL0 == 0x%x\n", ahd_inb(ahd, SEQCTL0));
+ ahd_dump_card_state(ahd);
+ ahd->msgout_buf[0] = MSG_BUS_DEV_RESET;
+ ahd->msgout_len = 1;
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd_assert_atn(ahd);
+ break;
+ }
+ case PROTO_VIOLATION:
+ {
+ struct scb *scb;
+ u_int scbid;
+ u_int scsiid;
+ u_int target;
+ u_int seq_flags;
+ u_int curphase;
+ int found;
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ scsiid = ahd_inb(ahd, SAVED_SCSIID);
+ target = SCSIID_TARGET(ahd, scsiid);
+ seq_flags = ahd_inb(ahd, SEQ_FLAGS);
+ curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+ if ((seq_flags & NOT_IDENTIFIED) != 0) {
+
+ /*
+ * The reconnecting target either did not send an
+ * identify message, or did, but we didn't find an SCB
+ * to match.
+ */
+ printf("%s:%c:%d: Target did not send an "
+ "IDENTIFY message. LASTPHASE = 0x%x, "
+ "SAVED_SCSIID == 0x%x\n", ahd_name(ahd),
+ 'A', target, ahd_inb(ahd, LASTPHASE),
+ scsiid);
+ } else if (scb == NULL) {
+ /*
+ * We don't seem to have an SCB active for this
+ * transaction. Print an error and reset the bus.
+ */
+ printf("%s:%c:%d: No SCB found during protocol "
+ "violation\n", ahd_name(ahd), 'A', target);
+ goto proto_violation_reset;
+ } else if ((seq_flags & NO_CDB_SENT) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("No or incomplete CDB sent to device.\n");
+ } else if ((ahd_inb(ahd, SCB_CONTROL) & STATUS_RCVD) == 0) {
+ /*
+ * The target never bothered to provide status to
+ * us prior to completing the command. Since we don't
+ * know the disposition of this command, we must attempt
+ * to abort it. Assert ATN and prepare to send an abort
+ * message.
+ */
+ ahd_print_path(ahd, scb);
+ printf("Completed command without status.\n");
+ } else {
+ ahd_print_path(ahd, scb);
+ printf("Unknown protocol violation.\n");
+ ahd_dump_card_state(ahd);
+ }
+ if ((curphase & ~P_DATAIN_DT) == 0) {
+proto_violation_reset:
+ /*
+ * Target either went directly to data
+ * phase or didn't respond to our ATN.
+ * The only safe thing to do is to blow
+ * it away with a bus reset.
+ */
+ found = ahd_reset_channel(ahd, 'A', TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahd_name(ahd), 'A', found);
+ } else {
+ /*
+ * Leave the selection hardware off in case
+ * this abort attempt will affect yet to
+ * be sent commands.
+ */
+ ahd_outb(ahd, SCSISEQ0,
+ ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+ ahd_print_path(ahd, scb);
+ printf("Protocol violation %s. Attempting to abort.\n",
+ ahd_lookup_phase_entry(curphase)->phasemsg);
+ scb->flags |= SCB_ABORT;
+ ahd_assert_atn(ahd);
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ }
+ return;
+ }
+ case IGN_WIDE_RES:
+ {
+ struct ahd_devinfo devinfo;
+
+ ahd_fetch_devinfo(ahd, &devinfo);
+ ahd_handle_ign_wide_residue(ahd, &devinfo);
+ break;
+ }
+ case BAD_PHASE:
+ {
+ u_int lastphase;
+
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ printf("%s:%c:%d: unknown scsi bus phase %x, "
+ "lastphase = 0x%x. Attempting to continue\n",
+ ahd_name(ahd), 'A',
+ SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
+ lastphase, ahd_inb(ahd, SCSISIGI));
+ break;
+ }
+ case MISSED_BUSFREE:
+ {
+ u_int lastphase;
+
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ printf("%s:%c:%d: Missed busfree. "
+ "Lastphase = 0x%x, Curphase = 0x%x\n",
+ ahd_name(ahd), 'A',
+ SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
+ lastphase, ahd_inb(ahd, SCSISIGI));
+ ahd_restart(ahd);
+ return;
+ }
+ case DATA_OVERRUN:
+ {
+ /*
+ * When the sequencer detects an overrun, it
+ * places the controller in "BITBUCKET" mode
+ * and allows the target to complete its transfer.
+ * Unfortunately, none of the counters get updated
+ * when the controller is in this mode, so we have
+ * no way of knowing how large the overrun was.
+ */
+ struct scb *scb;
+ u_int scbindex = ahd_get_scbptr(ahd);
+ u_int lastphase = ahd_inb(ahd, LASTPHASE);
+
+ scb = ahd_lookup_scb(ahd, scbindex);
+ ahd_print_path(ahd, scb);
+ printf("data overrun detected %s."
+ " Tag == 0x%x.\n",
+ ahd_lookup_phase_entry(lastphase)->phasemsg,
+ SCB_GET_TAG(scb));
+ ahd_print_path(ahd, scb);
+ printf("%s seen Data Phase. Length = %ld. NumSGs = %d.\n",
+ ahd_inb(ahd, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+ ahd_get_transfer_length(scb), scb->sg_count);
+ ahd_dump_sglist(scb);
+
+ /*
+ * Set this and it will take effect when the
+ * target does a command complete.
+ */
+ ahd_freeze_devq(ahd, scb);
+ ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ ahd_freeze_scb(scb);
+ break;
+ }
+ case MKMSG_FAILED:
+ {
+ struct ahd_devinfo devinfo;
+ struct scb *scb;
+ u_int scbid;
+
+ ahd_fetch_devinfo(ahd, &devinfo);
+ printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+ ahd_name(ahd), devinfo.channel, devinfo.target,
+ devinfo.lun);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL
+ && (scb->flags & SCB_RECOVERY_SCB) != 0)
+ /*
+ * Ensure that we didn't put a second instance of this
+ * SCB into the QINFIFO.
+ */
+ ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_REMOVE);
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb(ahd, SCB_CONTROL) & ~MK_MESSAGE);
+ break;
+ }
+ }
+ /*
+ * The sequencer is paused immediately on
+ * a SEQINT, so we should restart it when
+ * we're done.
+ */
+ ahd_unpause(ahd);
+}
+
+void
+ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
+{
+ struct scb *scb;
+ u_int status0;
+ u_int status3;
+ u_int status;
+ u_int lqistat1;
+ u_int lqostat0;
+ u_int scbid;
+
+ ahd_update_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ status3 = ahd_inb(ahd, SSTAT3) & (NTRAMPERR|OSRAMPERR);
+ status0 = ahd_inb(ahd, SSTAT0) & (IOERR|OVERRUN|SELDI|SELDO);
+ status = ahd_inb(ahd, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+ lqistat1 = ahd_inb(ahd, LQISTAT1);
+ lqostat0 = ahd_inb(ahd, LQOSTAT0);
+ if ((status0 & (SELDI|SELDO)) != 0) {
+ u_int simode0;
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ simode0 = ahd_inb(ahd, SIMODE0);
+ status0 &= simode0 & (ENSELDO|ENSELDI|IOERR);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ }
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL
+ && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+ scb = NULL;
+
+ /* Make sure the sequencer is in a safe location. */
+ ahd_clear_critical_section(ahd);
+
+ if ((status0 & IOERR) != 0) {
+ int now_lvd;
+
+ now_lvd = ahd_inb(ahd, SBLKCTL) & ENAB40;
+ printf("%s: Transceiver State Has Changed to %s mode\n",
+ ahd_name(ahd), now_lvd ? "LVD" : "SE");
+ ahd_outb(ahd, CLRSINT0, CLRIOERR);
+/* XXX Still True?? */
+ /*
+ * When transitioning to SE mode, the reset line
+ * glitches, triggering an arbitration bug in some
+ * Ultra2 controllers. This bug is cleared when we
+ * assert the reset line. Since a reset glitch has
+ * already occurred with this transition and a
+ * transceiver state change is handled just like
+ * a bus reset anyway, asserting the reset line
+ * ourselves is safe.
+ */
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/now_lvd == 0);
+
+ ahd_pause(ahd);
+ ahd_setup_iocell_workaround(ahd);
+ ahd_unpause(ahd);
+ } else if ((status0 & OVERRUN) != 0) {
+ printf("%s: SCSI offset overrun detected. Resetting bus.\n",
+ ahd_name(ahd));
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ } else if ((status & SCSIRSTI) != 0) {
+ printf("%s: Someone reset channel A\n", ahd_name(ahd));
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/FALSE);
+ } else if ((status & SCSIPERR) != 0) {
+ ahd_handle_transmission_error(ahd);
+ } else if (lqostat0 != 0) {
+ printf("%s: lqostat0 == 0x%x!\n", ahd_name(ahd), lqostat0);
+ ahd_outb(ahd, CLRLQOINT0, lqostat0);
+ if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+ ahd_outb(ahd, CLRLQOINT1, 0);
+ }
+ } else if ((status & SELTO) != 0) {
+ u_int scbid;
+
+ /* Stop the selection */
+ ahd_outb(ahd, SCSISEQ0, 0);
+
+ /* No more pending messages */
+ ahd_clear_msg_state(ahd);
+
+ /* Clear interrupt state */
+ ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) & ~ENBUSFREE);
+ ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+ /*
+ * Although the driver does not care about the
+ * 'Selection in Progress' status bit, the busy
+ * LED does. SELINGO is only cleared by a sucessfull
+ * selection, so we must manually clear it to insure
+ * the LED turns off just incase no future successful
+ * selections occur (e.g. no devices on the bus).
+ */
+ ahd_outb(ahd, CLRSINT0, CLRSELINGO);
+
+ scbid = ahd_inw(ahd, WAITING_TID_HEAD);
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_SELTO) != 0)
+ printf("%s: Saw Selection Timeout for SCB 0x%x\n",
+ ahd_name(ahd), scbid);
+#endif
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: ahd_intr - referenced scb not "
+ "valid during SELTO scb(0x%x)\n",
+ ahd_name(ahd), scbid);
+ ahd_dump_card_state(ahd);
+ panic("For diagnostics");
+ } else {
+ ahd_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+ ahd_freeze_devq(ahd, scb);
+ }
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_iocell_first_selection(ahd);
+ ahd_restart(ahd);
+ } else if ((status0 & (SELDI|SELDO)) != 0) {
+ ahd_iocell_first_selection(ahd);
+ ahd_unpause(ahd);
+ } else if (status3 != 0) {
+ printf("%s: SCSI Cell parity error SSTAT3 == 0x%x\n",
+ ahd_name(ahd), status3);
+ ahd_outb(ahd, CLRSINT3, status3);
+ } else if ((lqistat1 & (LQIPHASE_LQ|LQIPHASE_NLQ)) != 0) {
+ ahd_handle_lqiphase_error(ahd, lqistat1);
+ } else if ((status & BUSFREE) != 0) {
+ u_int busfreetime;
+ u_int lqostat1;
+ int restart;
+ int clear_fifo;
+ int packetized;
+ u_int mode;
+
+ /*
+ * Clear our selection hardware as soon as possible.
+ * We may have an entry in the waiting Q for this target,
+ * that is affected by this busfree and we don't want to
+ * go about selecting the target while we handle the event.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+
+ /*
+ * Determine what we were up to at the time of
+ * the busfree.
+ */
+ mode = AHD_MODE_SCSI;
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
+ lqostat1 = ahd_inb(ahd, LQOSTAT1);
+ switch (busfreetime) {
+ case BUSFREE_DFF0:
+ case BUSFREE_DFF1:
+ {
+ u_int scbid;
+ struct scb *scb;
+
+ mode = busfreetime == BUSFREE_DFF0
+ ? AHD_MODE_DFF0 : AHD_MODE_DFF1;
+ ahd_set_modes(ahd, mode, mode);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: Invalid SCB in DFF%d "
+ "during unexpected busfree\n",
+ ahd_name(ahd), mode);
+ packetized = 0;
+ } else
+ packetized = (scb->flags & SCB_PACKETIZED) != 0;
+ clear_fifo = 1;
+ break;
+ }
+ case BUSFREE_LQO:
+ clear_fifo = 0;
+ packetized = 1;
+ break;
+ default:
+ clear_fifo = 0;
+ packetized = (lqostat1 & LQOBUSFREE) != 0;
+ if (!packetized
+ && ahd_inb(ahd, LASTPHASE) == P_BUSFREE)
+ packetized = 1;
+ break;
+ }
+
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("Saw Busfree. Busfreetime = 0x%x.\n",
+ busfreetime);
+#endif
+ /*
+ * Busfrees that occur in non-packetized phases are
+ * handled by the nonpkt_busfree handler.
+ */
+ if (packetized && ahd_inb(ahd, LASTPHASE) == P_BUSFREE) {
+ restart = ahd_handle_pkt_busfree(ahd, busfreetime);
+ } else {
+ restart = ahd_handle_nonpkt_busfree(ahd);
+ }
+ /*
+ * Clear the busfree interrupt status. The setting of
+ * the interrupt is a pulse, so we do not need to muck
+ * with the ENBUSFREE logic. This also ensures that if
+ * the bus has moved on to another connection, busfree
+ * protection is still in force.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
+
+ if (clear_fifo)
+ ahd_clear_fifo(ahd, mode);
+
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ if (restart)
+ ahd_restart(ahd);
+ else {
+ ahd_unpause(ahd);
+ }
+ } else {
+ printf("%s: Missing case in ahd_handle_scsiint. status = %x\n",
+ ahd_name(ahd), status);
+ printf("%s: lqostat1 == 0x%x, SIMODE1 == 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, LQOSTAT1),
+ ahd_inb(ahd, SIMODE1));
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_dump_card_state(ahd);
+ panic("Missing SCSIINT case");
+ ahd_unpause(ahd);
+ }
+}
+
+static void
+ahd_handle_transmission_error(struct ahd_softc *ahd)
+{
+ u_int lqistat1;
+ u_int lqistat2;
+ u_int msg_out;
+ u_int curphase;
+ u_int lastphase;
+ u_int perrdiag;
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ lqistat1 = ahd_inb(ahd, LQISTAT1) & ~(LQIPHASE_LQ|LQIPHASE_NLQ);
+ lqistat2 = ahd_inb(ahd, LQISTAT2);
+ if ((lqistat1 & (LQICRCI_NLQ|LQICRCI_LQ)) == 0
+ && (ahd->bugs & AHD_NLQICRC_DELAYED_BUG) != 0) {
+ u_int lqistate;
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ lqistate = ahd_inb(ahd, LQISTATE);
+ if ((lqistate >= 0x1E && lqistate <= 0x24)
+ || (lqistate == 0x29)) {
+ printf("%s: NLQCRC found via LQISTATE\n",
+ ahd_name(ahd));
+ lqistat1 |= LQICRCI_NLQ;
+ }
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ }
+
+ ahd_outb(ahd, CLRLQIINT1, lqistat1);
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+ perrdiag = ahd_inb(ahd, PERRDIAG);
+ msg_out = MSG_INITIATOR_DET_ERR;
+ ahd_outb(ahd, CLRSINT1, CLRSCSIPERR);
+ printf("%s: Transmission error detected\n", ahd_name(ahd));
+ printf("%s: lqistat1 == 0x%x, LASTPHASE == 0x0%x, "
+ "curphase = 0x%x, perrdiag == 0x%x\n",
+ ahd_name(ahd), lqistat1, lastphase, curphase, perrdiag);
+ ahd_dump_card_state(ahd);
+ if ((lqistat1 & (LQIOVERI_LQ|LQIOVERI_NLQ)) != 0) {
+ printf("%s: Gross protocol error during incoming "
+ "packet. lqistat1 == 0x%x. Resetting bus.\n",
+ ahd_name(ahd), lqistat1);
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ return;
+ } else if ((lqistat1 & LQICRCI_LQ) != 0) {
+ /*
+ * A CRC error has been detected on an incoming LQ.
+ * The bus is currently hung on the last ACK.
+ * Hit LQIRETRY to release the last ack, and
+ * wait for the sequencer to determine that ATNO
+ * is asserted while in message out to take us
+ * to our host message loop. No NONPACKREQ or
+ * LQIPHASE type errors will occur in this
+ * scenario. After this first LQIRETRY, the LQI
+ * manager will be in ISELO where it will
+ * happily sit until another packet phase begins.
+ * Unexpected bus free detection is enabled
+ * through any phases that occur after we release
+ * this last ack until the LQI manager sees a
+ * packet phase. This implies we may have to
+ * ignore a perfectly valid "unexected busfree"
+ * after our "initiator detected error" message is
+ * sent. A busfree is the expected response after
+ * we tell the target that it's L_Q was corrupted.
+ * (SPI4R09 10.7.3.3.3)
+ */
+ ahd_outb(ahd, LQCTL2, LQIRETRY);
+ printf("LQIRetry for LQICRCI_LQ to release ACK\n");
+ } else if ((lqistat1 & LQICRCI_NLQ) != 0) {
+ u_int scbid;
+ struct scb *scb;
+
+ /*
+ * We detected a CRC error in a NON-LQ packet.
+ * The hardware has varying behavior in this situation
+ * depending on whether this packet was part of a
+ * stream or not.
+ *
+ * PKT by PKT mode:
+ * The hardware has already acked the complete packet.
+ * If the target honors our outstanding ATN condition,
+ * we should be (or soon will be) in MSGOUT phase.
+ * This will trigger the LQIPHASE_LQ status bit as the
+ * hardware was expecting another LQ. Unexpected
+ * busfree detection is enabled. Once LQIPHASE_LQ is
+ * true (first entry into host message loop is much
+ * the same), we must clear LQIPHASE_LQ and hit
+ * LQIRETRY so the hardware is ready to handle
+ * a future LQ. NONPACKREQ will not be asserted again
+ * once we hit LQIRETRY until another packet is
+ * processed. The target may either go busfree
+ * or start another packet in response to our message.
+ *
+ * Read Streaming P0 asserted:
+ * If we raise ATN and the target completes the entire
+ * stream (P0 asserted during the last packet), the
+ * hardware will ack all data and return to the ISTART
+ * state. When the target reponds to our ATN condition,
+ * LQIPHASE_LQ will be asserted. We should respond to
+ * this with an LQIRETRY to prepare for any future
+ * packets. NONPACKREQ will not be asserted again
+ * once we hit LQIRETRY until another packet is
+ * processed. The target may either go busfree or
+ * start another packet in response to our message.
+ * Busfree detection is enabled.
+ *
+ * Read Streaming P0 not asserted:
+ * If we raise ATN and the target transitions to
+ * MSGOUT in or after a packet where P0 is not
+ * asserted, the hardware will assert LQIPHASE_NLQ.
+ * We should respond to the LQIPHASE_NLQ with an
+ * LQICONTINUE. Should the target stay in a non-pkt
+ * phase after we send our message, the hardware
+ * will assert LQIPHASE_LQ. Recovery is then just as
+ * listed above for the read streaming with P0 asserted.
+ * Busfree detection is enabled.
+ */
+ printf("LQICRC_NLQ\n");
+ ahd_set_active_fifo(ahd);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: No SCB valid for LQICRC_NLQ. "
+ "Resetting bus\n", ahd_name(ahd));
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ return;
+ }
+ scb->flags |= SCB_TRANSMISSION_ERROR;
+ } else if ((lqistat1 & LQIBADLQI) != 0) {
+ printf("Need to handle BADLQI!\n");
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ return;
+ } else if ((perrdiag & (PARITYERR|LASTPHASE)) == PARITYERR) {
+ if ((curphase & ~P_DATAIN_DT) != 0) {
+ /* Ack the byte. So we can continue. */
+ printf("Acking %s to clear perror\n",
+ ahd_lookup_phase_entry(curphase)->phasemsg);
+ ahd_inb(ahd, SCSIDAT);
+ }
+
+ if (curphase == P_MESGIN)
+ msg_out = MSG_PARITY_ERROR;
+ }
+
+ /*
+ * We've set the hardware to assert ATN if we
+ * get a parity error on "in" phases, so all we
+ * need to do is stuff the message buffer with
+ * the appropriate message. "In" phases have set
+ * mesg_out to something other than MSG_NOP.
+ */
+ ahd->send_msg_perror = msg_out;
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_unpause(ahd);
+}
+
+static void
+ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1)
+{
+ /*
+ * Clear the sources of the interrupts.
+ */
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, CLRLQIINT1, lqistat1);
+
+ /*
+ * If the "illegal" phase changes were in response
+ * to our ATN to flag a CRC error, AND we ended up
+ * on packet boundaries, clear the error, restart the
+ * LQI manager as appropriate, and go on our merry
+ * way toward sending the message. Otherwise, reset
+ * the bus to clear the error.
+ */
+ ahd_set_active_fifo(ahd);
+ if ((ahd_inb(ahd, SCSISIGO) & ATNO) != 0
+ && (ahd_inb(ahd, MDFFSTAT) & DLZERO) != 0) {
+ if ((lqistat1 & LQIPHASE_LQ) != 0) {
+ printf("LQIRETRY for LQIPHASE_LQ\n");
+ ahd_outb(ahd, LQCTL2, LQIRETRY);
+ } else if ((lqistat1 & LQIPHASE_NLQ) != 0) {
+ printf("LQICONTINUE for LQIPHASE_NLQ\n");
+ ahd_outb(ahd, LQCTL2, LQIRETRY);
+ } else
+ panic("ahd_handle_lqiphase_error: No phase errors\n");
+ ahd_dump_card_state(ahd);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_unpause(ahd);
+ } else {
+ printf("Reseting Channel for LQI Phase error\n");
+ ahd_dump_card_state(ahd);
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ }
+}
+
+/*
+ * Packetized unexpected or expected busfree.
+ * Entered in MODE_SCSI.
+ */
+static int
+ahd_handle_pkt_busfree(struct ahd_softc *ahd, u_int busfreetime)
+{
+ u_int lqostat1;
+
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ lqostat1 = ahd_inb(ahd, LQOSTAT1);
+ if ((lqostat1 & LQOBUSFREE) != 0) {
+ struct scb *scb;
+ u_int scbid;
+ u_int waiting_h;
+ u_int waiting_t;
+ u_int next;
+
+ if ((busfreetime & BUSFREE_LQO) == 0)
+ printf("%s: Warning, BUSFREE time is 0x%x. "
+ "Expected BUSFREE_LQO.\n",
+ ahd_name(ahd), busfreetime);
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL)
+ panic("SCB not valid during LQOBUSFREE");
+ ahd_print_path(ahd, scb);
+ printf("Probable outgoing LQ CRC error. Retrying command\n");
+
+ /*
+ * Return the LQO manager to its idle loop. It will
+ * not do this automatically if the busfree occurs
+ * after the first REQ of either the LQ or command
+ * packet or between the LQ and command packet.
+ */
+ ahd_outb(ahd, LQCTL2, ahd_inb(ahd, LQCTL2) | LQOTOIDLE);
+
+ /*
+ * Clear the status.
+ */
+ ahd_outb(ahd, CLRLQOINT1, CLRLQOBUSFREE);
+ if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+ ahd_outb(ahd, CLRLQOINT1, 0);
+ }
+ /*
+ * The LQO manager detected an unexpected busfree
+ * either:
+ *
+ * 1) During an outgoing LQ.
+ * 2) After an outgoing LQ but before the first
+ * REQ of the command packet.
+ * 3) During an outgoing command packet.
+ *
+ * In all cases, CURRSCB is pointing to the
+ * SCB that encountered the failure. Clean
+ * up the queue, clear SELDO and LQOBUSFREE,
+ * and allow the sequencer to restart the select
+ * out at its lesure.
+ */
+ ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+ ahd_outb(ahd, CLRSINT0, CLRSELDO);
+ waiting_h = ahd_inw(ahd, WAITING_TID_HEAD);
+ if (waiting_h != scbid) {
+
+ ahd_outw(ahd, WAITING_TID_HEAD, scbid);
+ waiting_t = ahd_inw(ahd, WAITING_TID_TAIL);
+ next = SCB_LIST_NULL;
+ if (waiting_t == waiting_h) {
+ ahd_outw(ahd, WAITING_TID_TAIL, scbid);
+ } else {
+ ahd_set_scbptr(ahd, waiting_h);
+ next = ahd_inw(ahd, SCB_NEXT2);
+ }
+ ahd_set_scbptr(ahd, scbid);
+ ahd_outw(ahd, SCB_NEXT2, next);
+ }
+
+ /* Return unpausing the sequencer. */
+ return (0);
+ }
+ if (ahd->src_mode != AHD_MODE_SCSI) {
+ u_int scbid;
+ struct scb *scb;
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ ahd_print_path(ahd, scb);
+ printf("Unexpected PKT busfree condition\n");
+ ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb), 'A',
+ SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+ ROLE_INITIATOR, CAM_UNEXP_BUSFREE);
+
+ /* Return restarting the sequencer. */
+ return (1);
+ }
+ printf("%s: Unexpected PKT busfree condition\n", ahd_name(ahd));
+ ahd_dump_card_state(ahd);
+ /* Restart the sequencer. */
+ return (1);
+}
+
+/*
+ * Non-packetized unexpected or expected busfree.
+ */
+static int
+ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
+{
+ struct ahd_devinfo devinfo;
+ struct scb *scb;
+ u_int lastphase;
+ u_int saved_scsiid;
+ u_int saved_lun;
+ u_int target;
+ u_int initiator_role_id;
+ u_int scbid;
+ int printerror;
+
+ /*
+ * Look at what phase we were last in. If its message out,
+ * chances are pretty good that the busfree was in response
+ * to one of our abort requests.
+ */
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+ saved_lun = ahd_inb(ahd, SAVED_LUN);
+ target = SCSIID_TARGET(ahd, saved_scsiid);
+ initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+ ahd_compile_devinfo(&devinfo, initiator_role_id,
+ target, saved_lun, 'A', ROLE_INITIATOR);
+ printerror = 1;
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL
+ && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+ scb = NULL;
+
+ if (lastphase == P_MESGOUT) {
+ u_int tag;
+
+ tag = SCB_LIST_NULL;
+ if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT_TAG, TRUE)
+ || ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT, TRUE)) {
+ int found;
+ int sent_msg;
+
+ sent_msg = ahd->msgout_buf[ahd->msgout_index - 1];
+ ahd_print_path(ahd, scb);
+ printf("SCB %d - Abort%s Completed.\n",
+ SCB_GET_TAG(scb),
+ sent_msg == MSG_ABORT_TAG ? "" : " Tag");
+
+ if (sent_msg == MSG_ABORT_TAG)
+ tag = SCB_GET_TAG(scb);
+
+ if ((scb->flags & SCB_CMDPHASE_ABORT) != 0) {
+ /*
+ * This abort is in response to an
+ * unexpected switch to command phase
+ * for a packetized connection. Since
+ * the identify message was never sent,
+ * "saved lun" is 0. We really want to
+ * abort only the SCB that encountered
+ * this error, which could have a different
+ * lun. The SCB will be retried so the OS
+ * will see the UA after renegotiating to
+ * packetized.
+ */
+ tag = SCB_GET_TAG(scb);
+ saved_lun = scb->hscb->lun;
+ }
+ found = ahd_abort_scbs(ahd, target, 'A', saved_lun,
+ tag, ROLE_INITIATOR,
+ CAM_REQ_ABORTED);
+ printf("found == 0x%x\n", found);
+ printerror = 0;
+ } else if (ahd_sent_msg(ahd, AHDMSG_1B,
+ MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+ /*
+ * Don't mark the user's request for this BDR
+ * as completing with CAM_BDR_SENT. CAM3
+ * specifies CAM_REQ_CMP.
+ */
+ if (scb != NULL
+ && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+ && ahd_match_scb(ahd, scb, target, 'A',
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_INITIATOR))
+ ahd_set_transaction_status(scb, CAM_REQ_CMP);
+#endif
+ ahd_handle_devreset(ahd, &devinfo, CAM_BDR_SENT,
+ "Bus Device Reset",
+ /*verbose_level*/0);
+ printerror = 0;
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, FALSE)) {
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ /*
+ * PPR Rejected. Try non-ppr negotiation
+ * and retry command.
+ */
+ tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->goal.ppr_options = 0;
+ ahd_qinfifo_requeue_tail(ahd, scb);
+ printerror = 0;
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, FALSE)
+ || ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, FALSE)) {
+ /*
+ * Negotiation Rejected. Go-async and
+ * retry command.
+ */
+ ahd_set_width(ahd, &devinfo,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahd_set_syncrate(ahd, &devinfo,
+ /*period*/0, /*offset*/0,
+ /*ppr_options*/0,
+ AHD_TRANS_CUR|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahd_qinfifo_requeue_tail(ahd, scb);
+ printerror = 0;
+ } else if ((ahd->msg_flags & MSG_FLAG_EXPECT_IDE_BUSFREE) != 0
+ && ahd_sent_msg(ahd, AHDMSG_1B,
+ MSG_INITIATOR_DET_ERR, TRUE)) {
+
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Expected IDE Busfree\n");
+#endif
+ printerror = 0;
+ }
+ } else if (lastphase == P_MESGIN) {
+
+ if ((ahd->msg_flags & MSG_FLAG_EXPECT_PPR_BUSFREE) != 0) {
+
+ ahd_freeze_devq(ahd, scb);
+ ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+ ahd_freeze_scb(scb);
+ if ((ahd->msg_flags & MSG_FLAG_IU_REQ_CHANGED) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Now %spacketized.\n",
+ (scb->flags & SCB_PACKETIZED) == 0
+ ? "" : "non-");
+ ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQ_ABORTED);
+ } else {
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("PPR Negotiation Busfree.\n");
+#endif
+ ahd_done(ahd, scb);
+ }
+ printerror = 0;
+ }
+ }
+ if (printerror != 0) {
+ int aborted;
+
+ aborted = 0;
+ if (scb != NULL) {
+ u_int tag;
+
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ tag = SCB_GET_TAG(scb);
+ else
+ tag = SCB_LIST_NULL;
+ ahd_print_path(ahd, scb);
+ aborted = ahd_abort_scbs(ahd, target, 'A',
+ SCB_GET_LUN(scb), tag,
+ ROLE_INITIATOR,
+ CAM_UNEXP_BUSFREE);
+ } else {
+ /*
+ * We had not fully identified this connection,
+ * so we cannot abort anything.
+ */
+ printf("%s: ", ahd_name(ahd));
+ }
+ if (lastphase != P_BUSFREE)
+ ahd_force_renegotiation(ahd, &devinfo);
+ printf("Unexpected busfree %s, %d SCBs aborted, "
+ "PRGMCNT == 0x%x\n",
+ ahd_lookup_phase_entry(lastphase)->phasemsg,
+ aborted,
+ ahd_inb(ahd, PRGMCNT)
+ | (ahd_inb(ahd, PRGMCNT+1) << 8));
+ ahd_dump_card_state(ahd);
+ }
+ /* Always restart the sequencer. */
+ return (1);
+}
+
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahd_force_renegotiation(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ struct ahd_initiator_tinfo *targ_info;
+ struct ahd_tmode_tstate *tstate;
+
+ printf("Forcing renegotiation (%d:%c:%d)\n",
+ devinfo->our_scsiid, devinfo->channel,
+ devinfo->target);
+ targ_info = ahd_fetch_transinfo(ahd,
+ devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target,
+ &tstate);
+ ahd_update_neg_request(ahd, devinfo, tstate,
+ targ_info, /*force*/TRUE);
+}
+
+#define AHD_MAX_STEPS 2000
+void
+ahd_clear_critical_section(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ int stepping;
+ int steps;
+ u_int simode0;
+ u_int simode1;
+ u_int simode3;
+ u_int lqimode0;
+ u_int lqimode1;
+ u_int lqomode0;
+ u_int lqomode1;
+
+ if (ahd->num_critical_sections == 0)
+ return;
+
+ stepping = FALSE;
+ steps = 0;
+ simode0 = 0;
+ simode1 = 0;
+ simode3 = 0;
+ lqimode0 = 0;
+ lqimode1 = 0;
+ lqomode0 = 0;
+ lqomode1 = 0;
+ saved_modes = ahd_save_modes(ahd);
+ for (;;) {
+ struct cs *cs;
+ u_int seqaddr;
+ u_int i;
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ seqaddr = ahd_inb(ahd, CURADDR)
+ | (ahd_inb(ahd, CURADDR+1) << 8);
+
+ cs = ahd->critical_sections;
+ for (i = 0; i < ahd->num_critical_sections; i++, cs++) {
+
+ if (cs->begin < seqaddr && cs->end >= seqaddr)
+ break;
+ }
+
+ if (i == ahd->num_critical_sections)
+ break;
+
+ if (steps > AHD_MAX_STEPS) {
+ printf("%s: Infinite loop in critical section\n",
+ ahd_name(ahd));
+ ahd_dump_card_state(ahd);
+ panic("critical section loop");
+ }
+
+ steps++;
+ if (stepping == FALSE) {
+
+ /*
+ * Disable all interrupt sources so that the
+ * sequencer will not be stuck by a pausing
+ * interrupt condition while we attempt to
+ * leave a critical section.
+ */
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ simode0 = ahd_inb(ahd, SIMODE0);
+ simode3 = ahd_inb(ahd, SIMODE3);
+ lqimode0 = ahd_inb(ahd, LQIMODE0);
+ lqimode1 = ahd_inb(ahd, LQIMODE1);
+ lqomode0 = ahd_inb(ahd, LQOMODE0);
+ lqomode1 = ahd_inb(ahd, LQOMODE1);
+ ahd_outb(ahd, SIMODE0, 0);
+ ahd_outb(ahd, SIMODE3, 0);
+ ahd_outb(ahd, LQIMODE0, 0);
+ ahd_outb(ahd, LQIMODE1, 0);
+ ahd_outb(ahd, LQOMODE0, 0);
+ ahd_outb(ahd, LQOMODE1, 0);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ simode1 = ahd_inb(ahd, SIMODE1);
+ ahd_outb(ahd, SIMODE1, 0);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) | STEP);
+ stepping = TRUE;
+ }
+ ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+ ahd_outb(ahd, HCNTRL, ahd->unpause);
+ do {
+ ahd_delay(200);
+ } while (!ahd_is_paused(ahd));
+ ahd_update_modes(ahd);
+ }
+ if (stepping) {
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ ahd_outb(ahd, SIMODE0, simode0);
+ ahd_outb(ahd, SIMODE3, simode3);
+ ahd_outb(ahd, LQIMODE0, lqimode0);
+ ahd_outb(ahd, LQIMODE1, lqimode1);
+ ahd_outb(ahd, LQOMODE0, lqomode0);
+ ahd_outb(ahd, LQOMODE1, lqomode1);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, SIMODE1, simode1);
+ ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) & ~STEP);
+ }
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahd_clear_intstat(struct ahd_softc *ahd)
+{
+ /* Clear any interrupt conditions this may have caused */
+ ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+ |CLRBUSFREE|CLRSCSIPERR|CLRREQINIT);
+ ahd_outb(ahd, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO|CLRIOERR);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHD_DEBUG
+uint32_t ahd_debug = AHD_DEBUG;
+#endif
+void
+ahd_print_scb(struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ int i;
+
+ hscb = scb->hscb;
+ printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+ (void *)scb,
+ hscb->control,
+ hscb->scsiid,
+ hscb->lun,
+ hscb->cdb_len);
+ printf("Shared Data: ");
+ for (i = 0; i < sizeof(hscb->shared_data.idata.cdb); i++)
+ printf("%#02x", hscb->shared_data.idata.cdb[i]);
+ printf(" dataptr:%#x%x datacnt:%#x sgptr:%#x tag:%#x\n",
+ (uint32_t)((ahd_le64toh(hscb->dataptr) >> 32) & 0xFFFFFFFF),
+ (uint32_t)(ahd_le64toh(hscb->dataptr) & 0xFFFFFFFF),
+ ahd_le32toh(hscb->datacnt),
+ ahd_le32toh(hscb->sgptr),
+ SCB_GET_TAG(scb));
+ ahd_dump_sglist(scb);
+}
+
+void
+ahd_dump_sglist(struct scb *scb)
+{
+ int i;
+
+ if (scb->sg_count > 0) {
+ if ((scb->ahd_softc->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg_list;
+
+ sg_list = (struct ahd_dma64_seg*)scb->sg_list;
+ for (i = 0; i < scb->sg_count; i++) {
+ uint64_t addr;
+
+ addr = ahd_le64toh(sg_list[i].addr);
+ printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+ i,
+ (uint32_t)((addr >> 32) & 0xFFFFFFFF),
+ (uint32_t)(addr & 0xFFFFFFFF),
+ ahd_le32toh(sg_list[i].len));
+ }
+ } else {
+ struct ahd_dma_seg *sg_list;
+
+ sg_list = (struct ahd_dma_seg*)scb->sg_list;
+ for (i = 0; i < scb->sg_count; i++) {
+ printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+ i,
+ (ahd_le32toh(sg_list[i].len) >> 24
+ & SG_HIGH_ADDR_BITS),
+ ahd_le32toh(sg_list[i].addr),
+ ahd_le32toh(sg_list[i].len)
+ & AHD_SG_LEN_MASK);
+ }
+ }
+ }
+}
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahd_tmode_tstate *
+ahd_alloc_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel)
+{
+ struct ahd_tmode_tstate *master_tstate;
+ struct ahd_tmode_tstate *tstate;
+ int i;
+
+ master_tstate = ahd->enabled_targets[ahd->our_id];
+ if (ahd->enabled_targets[scsi_id] != NULL
+ && ahd->enabled_targets[scsi_id] != master_tstate)
+ panic("%s: ahd_alloc_tstate - Target already allocated",
+ ahd_name(ahd));
+ tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
+ if (tstate == NULL)
+ return (NULL);
+
+ /*
+ * If we have allocated a master tstate, copy user settings from
+ * the master tstate (taken from SRAM or the EEPROM) for this
+ * channel, but reset our current and goal settings to async/narrow
+ * until an initiator talks to us.
+ */
+ if (master_tstate != NULL) {
+ memcpy(tstate, master_tstate, sizeof(*tstate));
+ memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+ for (i = 0; i < 16; i++) {
+ memset(&tstate->transinfo[i].curr, 0,
+ sizeof(tstate->transinfo[i].curr));
+ memset(&tstate->transinfo[i].goal, 0,
+ sizeof(tstate->transinfo[i].goal));
+ }
+ } else
+ memset(tstate, 0, sizeof(*tstate));
+ ahd->enabled_targets[scsi_id] = tstate;
+ return (tstate);
+}
+
+#ifdef AHD_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahd_free_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel, int force)
+{
+ struct ahd_tmode_tstate *tstate;
+
+ /*
+ * Don't clean up our "master" tstate.
+ * It has our default user settings.
+ */
+ if (scsi_id == ahd->our_id
+ && force == FALSE)
+ return;
+
+ tstate = ahd->enabled_targets[scsi_id];
+ if (tstate != NULL)
+ free(tstate, M_DEVBUF);
+ ahd->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest period to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+void
+ahd_devlimited_syncrate(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *tinfo,
+ u_int *period, u_int *ppr_options, role_t role)
+{
+ struct ahd_transinfo *transinfo;
+ u_int maxsync;
+
+ if ((ahd_inb(ahd, SBLKCTL) & ENAB40) != 0
+ && (ahd_inb(ahd, SSTAT2) & EXP_ACTIVE) == 0) {
+ maxsync = AHD_SYNCRATE_PACED;
+ } else {
+ maxsync = AHD_SYNCRATE_ULTRA;
+ /* Can't do DT related options on an SE bus */
+ *ppr_options &= MSG_EXT_PPR_QAS_REQ;
+ }
+ /*
+ * Never allow a value higher than our current goal
+ * period otherwise we may allow a target initiated
+ * negotiation to go above the limit as set by the
+ * user. In the case of an initiator initiated
+ * sync negotiation, we limit based on the user
+ * setting. This allows the system to still accept
+ * incoming negotiations even if target initiated
+ * negotiation is not performed.
+ */
+ if (role == ROLE_TARGET)
+ transinfo = &tinfo->user;
+ else
+ transinfo = &tinfo->goal;
+ *ppr_options &= (transinfo->ppr_options|MSG_EXT_PPR_PCOMP_EN);
+ if (transinfo->period == 0) {
+ *period = 0;
+ *ppr_options = 0;
+ } else {
+ *period = MAX(*period, transinfo->period);
+ ahd_find_syncrate(ahd, period, ppr_options, maxsync);
+ }
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+void
+ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
+ u_int *ppr_options, u_int maxsync)
+{
+ /* Skip all PACED only entries if IU is not available */
+ if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0
+ && maxsync < AHD_SYNCRATE_DT)
+ maxsync = AHD_SYNCRATE_DT;
+
+ /* Skip all DT only entries if DT is not available */
+ if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && maxsync < AHD_SYNCRATE_ULTRA2)
+ maxsync = AHD_SYNCRATE_ULTRA2;
+
+ if (*period < maxsync)
+ *period = maxsync;
+
+ if ((*ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+ && *period > AHD_SYNCRATE_MIN_DT)
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+ if (*period > AHD_SYNCRATE_MIN)
+ *period = 0;
+
+ /* Honor PPR option conformance rules. */
+ if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0)
+ *ppr_options &= (MSG_EXT_PPR_DT_REQ|MSG_EXT_PPR_QAS_REQ);
+
+ if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0)
+ *ppr_options &= MSG_EXT_PPR_QAS_REQ;
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+void
+ahd_validate_offset(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *tinfo,
+ u_int period, u_int *offset, int wide,
+ role_t role)
+{
+ u_int maxoffset;
+
+ /* Limit offset to what we can do */
+ if (period == 0)
+ maxoffset = 0;
+ else if (period <= AHD_SYNCRATE_PACED)
+ maxoffset = MAX_OFFSET_PACED;
+ else
+ maxoffset = MAX_OFFSET;
+ *offset = MIN(*offset, maxoffset);
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *offset = MIN(*offset, tinfo->user.offset);
+ else
+ *offset = MIN(*offset, tinfo->goal.offset);
+ }
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+void
+ahd_validate_width(struct ahd_softc *ahd, struct ahd_initiator_tinfo *tinfo,
+ u_int *bus_width, role_t role)
+{
+ switch (*bus_width) {
+ default:
+ if (ahd->features & AHD_WIDE) {
+ /* Respond Wide */
+ *bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ break;
+ }
+ /* FALLTHROUGH */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ *bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *bus_width = MIN(tinfo->user.width, *bus_width);
+ else
+ *bus_width = MIN(tinfo->goal.width, *bus_width);
+ }
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient oportunity. This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahd_update_neg_request(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct ahd_tmode_tstate *tstate,
+ struct ahd_initiator_tinfo *tinfo, int force)
+{
+ u_int auto_negotiate_orig;
+
+ auto_negotiate_orig = tstate->auto_negotiate;
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+ || (force
+ && (tinfo->goal.period != 0
+ || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+ || tinfo->goal.ppr_options != 0)))
+ tstate->auto_negotiate |= devinfo->target_mask;
+ else
+ tstate->auto_negotiate &= ~devinfo->target_mask;
+
+ return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahd_set_syncrate(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int period, u_int offset, u_int ppr_options,
+ u_int type, int paused)
+{
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int old_period;
+ u_int old_offset;
+ u_int old_ppr;
+ int active;
+ int update_needed;
+
+ active = (type & AHD_TRANS_ACTIVE) == AHD_TRANS_ACTIVE;
+ update_needed = 0;
+
+ if (period == 0 || offset == 0) {
+ period = 0;
+ offset = 0;
+ }
+
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if ((type & AHD_TRANS_USER) != 0) {
+ tinfo->user.period = period;
+ tinfo->user.offset = offset;
+ tinfo->user.ppr_options = ppr_options;
+ }
+
+ if ((type & AHD_TRANS_GOAL) != 0) {
+ tinfo->goal.period = period;
+ tinfo->goal.offset = offset;
+ tinfo->goal.ppr_options = ppr_options;
+ }
+
+ old_period = tinfo->curr.period;
+ old_offset = tinfo->curr.offset;
+ old_ppr = tinfo->curr.ppr_options;
+
+ if ((type & AHD_TRANS_CUR) != 0
+ && (old_period != period
+ || old_offset != offset
+ || old_ppr != ppr_options)) {
+
+ update_needed++;
+
+ tinfo->curr.period = period;
+ tinfo->curr.offset = offset;
+ tinfo->curr.ppr_options = ppr_options;
+
+ ahd_send_async(ahd, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ if (offset != 0) {
+ printf("%s: target %d synchronous with "
+ "period = 0x%x, offset = 0x%x%s\n",
+ ahd_name(ahd), devinfo->target,
+ period, offset,
+ (ppr_options & MSG_EXT_PPR_DT_REQ)
+ ? " (DT)" : "");
+ } else {
+ printf("%s: target %d using "
+ "asynchronous transfers\n",
+ ahd_name(ahd), devinfo->target);
+ }
+ }
+ }
+ /*
+ * Always refresh the neg-table to handle the case of the
+ * sequencer setting the ENATNO bit for a MK_MESSAGE request.
+ * We will always renegotiate in that case if this is a
+ * packetized request.
+ */
+ if ((type & AHD_TRANS_CUR) != 0)
+ ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+
+ update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+ tinfo, /*force*/FALSE);
+
+ if (update_needed)
+ ahd_update_pending_scbs(ahd);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahd_set_width(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int width, u_int type, int paused)
+{
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int oldwidth;
+ int active;
+ int update_needed;
+
+ active = (type & AHD_TRANS_ACTIVE) == AHD_TRANS_ACTIVE;
+ update_needed = 0;
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if ((type & AHD_TRANS_USER) != 0)
+ tinfo->user.width = width;
+
+ if ((type & AHD_TRANS_GOAL) != 0)
+ tinfo->goal.width = width;
+
+ oldwidth = tinfo->curr.width;
+ if ((type & AHD_TRANS_CUR) != 0 && oldwidth != width) {
+
+ update_needed++;
+
+ tinfo->curr.width = width;
+ ahd_send_async(ahd, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ printf("%s: target %d using %dbit transfers\n",
+ ahd_name(ahd), devinfo->target,
+ 8 * (0x01 << width));
+ }
+ }
+ if ((type & AHD_TRANS_CUR) != 0)
+ ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+
+ update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+ tinfo, /*force*/FALSE);
+ if (update_needed)
+ ahd_update_pending_scbs(ahd);
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+void
+ahd_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ ahd_queue_alg alg)
+{
+ ahd_platform_set_tags(ahd, devinfo, alg);
+ ahd_send_async(ahd, devinfo->channel, devinfo->target,
+ devinfo->lun, AC_TRANSFER_NEG, &alg);
+}
+
+static void
+ahd_update_neg_table(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct ahd_transinfo *tinfo)
+{
+ ahd_mode_state saved_modes;
+ u_int period;
+ u_int ppr_opts;
+ u_int con_opts;
+ u_int offset;
+ u_int precomp;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ ahd_outb(ahd, NEGOADDR, devinfo->target);
+ period = tinfo->period;
+ offset = tinfo->offset;
+ precomp = 0;
+ if (period == 0)
+ period = AHD_SYNCRATE_ASYNC;
+ if (period == AHD_SYNCRATE_160) {
+ period = AHD_SYNCRATE_REVA_160;
+ precomp = 0;
+ if ((ahd->flags & AHD_CPQ_BOARD) == 0)
+ precomp |= AHD_PRECOMP_FASTSLEW;
+ if ((tinfo->ppr_options & MSG_EXT_PPR_PCOMP_EN) != 0)
+ precomp |= AHD_PRECOMP_CUTBACK_29;
+ }
+ ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_PRECOMP);
+ ahd_outb(ahd, ANNEXDAT, precomp);
+
+ ahd_outb(ahd, NEGPERIOD, period);
+ ppr_opts = tinfo->ppr_options
+ & (MSG_EXT_PPR_QAS_REQ|MSG_EXT_PPR_DT_REQ|MSG_EXT_PPR_IU_REQ);
+ /*
+ * When the SPI4 spec was finalized, PACE transfers
+ * was not made a configurable option in the PPR message.
+ * Instead it is assumed to be enabled for any
+ * syncrate faster than 80MHz. Nevertheless, Harpoon
+ * allows this to be configurable.
+ *
+ * Harpoon also assumes at most 2 data bytes per negotiated
+ * REQ/ACK offset. Paced transfers take 4, so we must
+ * adjust our offset.
+ */
+ if (period <= AHD_SYNCRATE_PACED) {
+ ppr_opts |= PPROPT_PACE;
+ offset *= 2;
+ }
+ ahd_outb(ahd, NEGPPROPTS, ppr_opts);
+ ahd_outb(ahd, NEGOFFSET, offset);
+
+ con_opts = 0;
+ if (tinfo->width == MSG_EXT_WDTR_BUS_16_BIT)
+ con_opts |= WIDEXFER;
+
+ /*
+ * During packetized transfers, the target will
+ * give us the oportunity to send command packets
+ * without us asserting attention.
+ */
+ if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) == 0)
+ con_opts |= ENAUTOATNO;
+ ahd_outb(ahd, NEGCONOPTS, con_opts);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * When the transfer settings for a connection change, update any
+ * in-transit SCBs to contain the new data so the hardware will
+ * be set correctly during future (re)selections.
+ */
+static void
+ahd_update_pending_scbs(struct ahd_softc *ahd)
+{
+ struct scb *pending_scb;
+ int pending_scb_count;
+ int i;
+ int paused;
+ u_int saved_scbptr;
+ ahd_mode_state saved_modes;
+
+ /*
+ * Traverse the pending SCB list and ensure that all of the
+ * SCBs there have the proper settings.
+ */
+ pending_scb_count = 0;
+ LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+ struct ahd_devinfo devinfo;
+ struct hardware_scb *pending_hscb;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ ahd_scb_devinfo(ahd, &devinfo, pending_scb);
+ tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ pending_hscb = pending_scb->hscb;
+ if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+ && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+ pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+ pending_hscb->control &= ~MK_MESSAGE;
+ }
+ ahd_sync_scb(ahd, pending_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ pending_scb_count++;
+ }
+
+ if (pending_scb_count == 0)
+ return;
+
+ if (ahd_is_paused(ahd)) {
+ paused = 1;
+ } else {
+ paused = 0;
+ ahd_pause(ahd);
+ }
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ saved_scbptr = ahd_get_scbptr(ahd);
+ /* Ensure that the hscbs down on the card match the new information */
+ for (i = 0; i < ahd->scb_data.maxhscbs; i++) {
+ struct hardware_scb *pending_hscb;
+ u_int control;
+ u_int scb_tag;
+
+ ahd_set_scbptr(ahd, i);
+ scb_tag = i;
+ pending_scb = ahd_lookup_scb(ahd, scb_tag);
+ if (pending_scb == NULL)
+ continue;
+
+ pending_hscb = pending_scb->hscb;
+ control = ahd_inb_scbram(ahd, SCB_CONTROL);
+ control &= ~MK_MESSAGE;
+ control |= pending_hscb->control & MK_MESSAGE;
+ ahd_outb(ahd, SCB_CONTROL, control);
+ }
+ ahd_set_scbptr(ahd,saved_scbptr);
+ ahd_restore_modes(ahd, saved_modes);
+
+ if (paused == 0)
+ ahd_unpause(ahd);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahd_fetch_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ ahd_mode_state saved_modes;
+ u_int saved_scsiid;
+ role_t role;
+ int our_id;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ if (ahd_inb(ahd, SSTAT0) & TARGET)
+ role = ROLE_TARGET;
+ else
+ role = ROLE_INITIATOR;
+
+ if (role == ROLE_TARGET
+ && (ahd_inb(ahd, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
+ /* We were selected, so pull our id from TARGIDIN */
+ our_id = ahd_inb(ahd, TARGIDIN) & OID;
+ } else if (role == ROLE_TARGET)
+ our_id = ahd_inb(ahd, TOWNID);
+ else
+ our_id = ahd_inb(ahd, IOWNID);
+
+ saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+ ahd_compile_devinfo(devinfo,
+ our_id,
+ SCSIID_TARGET(ahd, saved_scsiid),
+ ahd_inb(ahd, SAVED_LUN),
+ SCSIID_CHANNEL(ahd, saved_scsiid),
+ role);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+struct ahd_phase_table_entry*
+ahd_lookup_phase_entry(int phase)
+{
+ struct ahd_phase_table_entry *entry;
+ struct ahd_phase_table_entry *last_entry;
+
+ /*
+ * num_phases doesn't include the default entry which
+ * will be returned if the phase doesn't match.
+ */
+ last_entry = &ahd_phase_table[num_phases];
+ for (entry = ahd_phase_table; entry < last_entry; entry++) {
+ if (phase == entry->phase)
+ break;
+ }
+ return (entry);
+}
+
+void
+ahd_compile_devinfo(struct ahd_devinfo *devinfo, u_int our_id, u_int target,
+ u_int lun, char channel, role_t role)
+{
+ devinfo->our_scsiid = our_id;
+ devinfo->target = target;
+ devinfo->lun = lun;
+ devinfo->target_offset = target;
+ devinfo->channel = channel;
+ devinfo->role = role;
+ if (channel == 'B')
+ devinfo->target_offset += 8;
+ devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+static void
+ahd_scb_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct scb *scb)
+{
+ role_t role;
+ int our_id;
+
+ our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+ role = ROLE_INITIATOR;
+ if ((scb->hscb->control & TARGET_SCB) != 0)
+ role = ROLE_TARGET;
+ ahd_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahd, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted. Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahd_setup_initiator_msgout(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct scb *scb)
+{
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+
+ if (ahd_currently_packetized(ahd))
+ ahd->msg_flags |= MSG_FLAG_PACKETIZED;
+
+ if (ahd->send_msg_perror
+ && ahd_inb(ahd, MSG_OUT) == HOST_MSG) {
+ ahd->msgout_buf[ahd->msgout_index++] = ahd->send_msg_perror;
+ ahd->msgout_len++;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ printf("Setting up for Parity Error delivery\n");
+ return;
+ } else if (scb == NULL) {
+ printf("%s: WARNING. No pending message for "
+ "I_T msgin. Issuing NO-OP\n", ahd_name(ahd));
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_NOOP;
+ ahd->msgout_len++;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ return;
+ }
+
+ if ((scb->flags & SCB_DEVICE_RESET) == 0
+ && (scb->flags & SCB_PACKETIZED) == 0
+ && ahd_inb(ahd, MSG_OUT) == MSG_IDENTIFYFLAG) {
+ u_int identify_msg;
+
+ identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+ if ((scb->hscb->control & DISCENB) != 0)
+ identify_msg |= MSG_IDENTIFY_DISCFLAG;
+ ahd->msgout_buf[ahd->msgout_index++] = identify_msg;
+ ahd->msgout_len++;
+
+ if ((scb->hscb->control & TAG_ENB) != 0) {
+ ahd->msgout_buf[ahd->msgout_index++] =
+ scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+ ahd->msgout_buf[ahd->msgout_index++] = SCB_GET_TAG(scb);
+ ahd->msgout_len += 2;
+ }
+ }
+
+ if (scb->flags & SCB_DEVICE_RESET) {
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_BUS_DEV_RESET;
+ ahd->msgout_len++;
+ ahd_print_path(ahd, scb);
+ printf("Bus Device Reset Message Sent\n");
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+ } else if ((scb->flags & SCB_ABORT) != 0) {
+
+ if ((scb->hscb->control & TAG_ENB) != 0) {
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT_TAG;
+ } else {
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT;
+ }
+ ahd->msgout_len++;
+ ahd_print_path(ahd, scb);
+ printf("Abort%s Message Sent\n",
+ (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+ } else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
+ ahd_build_transfer_msg(ahd, devinfo);
+ /*
+ * Clear our selection hardware in advance of potential
+ * PPR IU status change busfree. We may have an entry in
+ * the waiting Q for this target, and we don't want to go
+ * about selecting while we handle the busfree and blow
+ * it away.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+ } else {
+ printf("ahd_intr: AWAITING_MSG for an SCB that "
+ "does not have a waiting message\n");
+ printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+ devinfo->target_mask);
+ panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
+ "SCB flags = %x", SCB_GET_TAG(scb), scb->hscb->control,
+ ahd_inb(ahd, MSG_OUT), scb->flags);
+ }
+
+ /*
+ * Clear the MK_MESSAGE flag from the SCB so we aren't
+ * asked to send this message again.
+ */
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb_scbram(ahd, SCB_CONTROL) & ~MK_MESSAGE);
+ scb->hscb->control &= ~MK_MESSAGE;
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahd_build_transfer_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ /*
+ * We need to initiate transfer negotiations.
+ * If our current and goal settings are identical,
+ * we want to renegotiate due to a check condition.
+ */
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ int dowide;
+ int dosync;
+ int doppr;
+ int use_ppr;
+ u_int period;
+ u_int ppr_options;
+ u_int offset;
+
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ /*
+ * Filter our period based on the current connection.
+ * If we can't perform DT transfers on this segment (not in LVD
+ * mode for instance), then our decision to issue a PPR message
+ * may change.
+ */
+ period = tinfo->goal.period;
+ ppr_options = tinfo->goal.ppr_options;
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ ppr_options = 0;
+ ahd_devlimited_syncrate(ahd, tinfo, &period,
+ &ppr_options, devinfo->role);
+ dowide = tinfo->curr.width != tinfo->goal.width;
+ dosync = tinfo->curr.period != period;
+ doppr = tinfo->curr.ppr_options != ppr_options;
+
+ if (!dowide && !dosync && !doppr) {
+ dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+ dosync = tinfo->goal.period != 0;
+ doppr = tinfo->goal.ppr_options != 0;
+ }
+
+ if (!dowide && !dosync && !doppr) {
+ panic("ahd_intr: AWAITING_MSG for negotiation, "
+ "but no negotiation needed\n");
+ }
+
+ use_ppr = (tinfo->curr.transport_version >= 3) || doppr;
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ use_ppr = 0;
+
+ /*
+ * Both the PPR message and SDTR message require the
+ * goal syncrate to be limited to what the target device
+ * is capable of handling (based on whether an LVD->SE
+ * expander is on the bus), so combine these two cases.
+ * Regardless, guarantee that if we are using WDTR and SDTR
+ * messages that WDTR comes first.
+ */
+ if (use_ppr || (dosync && !dowide)) {
+
+ offset = tinfo->goal.offset;
+ ahd_validate_offset(ahd, tinfo, period, &offset,
+ use_ppr ? tinfo->goal.width
+ : tinfo->curr.width,
+ devinfo->role);
+ if (use_ppr) {
+ ahd_construct_ppr(ahd, devinfo, period, offset,
+ tinfo->goal.width, ppr_options);
+ } else {
+ ahd_construct_sdtr(ahd, devinfo, period, offset);
+ }
+ } else {
+ ahd_construct_wdtr(ahd, devinfo, tinfo->goal.width);
+ }
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_sdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int period, u_int offset)
+{
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXTENDED;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_SDTR_LEN;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_SDTR;
+ ahd->msgout_buf[ahd->msgout_index++] = period;
+ ahd->msgout_buf[ahd->msgout_index++] = offset;
+ ahd->msgout_len += 5;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+ ahd_name(ahd), devinfo->channel, devinfo->target,
+ devinfo->lun, period, offset);
+ }
+}
+
+/*
+ * Build a wide negotiateion message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_wdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int bus_width)
+{
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXTENDED;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_WDTR_LEN;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_WDTR;
+ ahd->msgout_buf[ahd->msgout_index++] = bus_width;
+ ahd->msgout_len += 4;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending WDTR %x\n",
+ ahd_name(ahd), devinfo->channel, devinfo->target,
+ devinfo->lun, bus_width);
+ }
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_ppr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int period, u_int offset, u_int bus_width,
+ u_int ppr_options)
+{
+ /*
+ * Always request precompensation from
+ * the other target if we are running
+ * at paced syncrates.
+ */
+ if (period <= AHD_SYNCRATE_PACED)
+ ppr_options |= MSG_EXT_PPR_PCOMP_EN;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXTENDED;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_PPR_LEN;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_PPR;
+ ahd->msgout_buf[ahd->msgout_index++] = period;
+ ahd->msgout_buf[ahd->msgout_index++] = 0;
+ ahd->msgout_buf[ahd->msgout_index++] = offset;
+ ahd->msgout_buf[ahd->msgout_index++] = bus_width;
+ ahd->msgout_buf[ahd->msgout_index++] = ppr_options;
+ ahd->msgout_len += 8;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+ "offset %x, ppr_options %x\n", ahd_name(ahd),
+ devinfo->channel, devinfo->target, devinfo->lun,
+ bus_width, period, offset, ppr_options);
+ }
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahd_clear_msg_state(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd->send_msg_perror = 0;
+ ahd->msg_flags = MSG_FLAG_NONE;
+ ahd->msgout_len = 0;
+ ahd->msgin_index = 0;
+ ahd->msg_type = MSG_TYPE_NONE;
+ if ((ahd_inb(ahd, SCSISIGO) & ATNO) != 0) {
+ /*
+ * The target didn't care to respond to our
+ * message request, so clear ATN.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRATNO);
+ }
+ ahd_outb(ahd, MSG_OUT, MSG_NOOP);
+ ahd_outb(ahd, SEQ_FLAGS2,
+ ahd_inb(ahd, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahd_handle_message_phase(struct ahd_softc *ahd)
+{
+ struct ahd_devinfo devinfo;
+ u_int bus_phase;
+ int end_session;
+
+ ahd_fetch_devinfo(ahd, &devinfo);
+ end_session = FALSE;
+ bus_phase = ahd_inb(ahd, LASTPHASE);
+
+ if ((ahd_inb(ahd, LQISTAT2) & LQIPHASE_OUTPKT) != 0) {
+ printf("LQIRETRY for LQIPHASE_OUTPKT\n");
+ ahd_outb(ahd, LQCTL2, LQIRETRY);
+ }
+reswitch:
+ switch (ahd->msg_type) {
+ case MSG_TYPE_INITIATOR_MSGOUT:
+ {
+ int lastbyte;
+ int phasemis;
+ int msgdone;
+
+ if (ahd->msgout_len == 0 && ahd->send_msg_perror == 0)
+ panic("HOST_MSG_LOOP interrupt with no active message");
+
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("%s: INITIATOR_MSG_OUT", ahd_name(ahd));
+#endif
+ phasemis = bus_phase != P_MESGOUT;
+ if (phasemis) {
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s - %s\n",
+ ahd_lookup_phase_entry(bus_phase)
+ ->phasemsg,
+ ahd_lookup_phase_entry(ahd_inb(ahd, SCSISIGI) & PHASE_MASK)
+ ->phasemsg);
+ }
+#endif
+ if (bus_phase == P_MESGIN) {
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRATNO);
+ ahd->send_msg_perror = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+ ahd->msgin_index = 0;
+ goto reswitch;
+ }
+ end_session = TRUE;
+ break;
+ }
+
+ if (ahd->send_msg_perror) {
+ ahd_outb(ahd, CLRSINT1, CLRATNO);
+ ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n", ahd->send_msg_perror);
+#endif
+ /*
+ * If we are notifying the target of a CRC error
+ * during packetized operations, the target is
+ * within its rights to acknowledge our message
+ * with a busfree.
+ */
+ if ((ahd->msg_flags & MSG_FLAG_PACKETIZED) != 0
+ && ahd->send_msg_perror == MSG_INITIATOR_DET_ERR)
+ ahd->msg_flags |= MSG_FLAG_EXPECT_IDE_BUSFREE;
+
+ ahd_outb(ahd, RETURN_2, ahd->send_msg_perror);
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_WRITE);
+ break;
+ }
+
+ msgdone = ahd->msgout_index == ahd->msgout_len;
+ if (msgdone) {
+ /*
+ * The target has requested a retry.
+ * Re-assert ATN, reset our message index to
+ * 0, and try again.
+ */
+ ahd->msgout_index = 0;
+ ahd_assert_atn(ahd);
+ }
+
+ lastbyte = ahd->msgout_index == (ahd->msgout_len - 1);
+ if (lastbyte) {
+ /* Last byte is signified by dropping ATN */
+ ahd_outb(ahd, CLRSINT1, CLRATNO);
+ }
+
+ /*
+ * Clear our interrupt status and present
+ * the next byte on the bus.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahd->msgout_buf[ahd->msgout_index]);
+#endif
+ ahd_outb(ahd, RETURN_2, ahd->msgout_buf[ahd->msgout_index++]);
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_WRITE);
+ break;
+ }
+ case MSG_TYPE_INITIATOR_MSGIN:
+ {
+ int phasemis;
+ int message_done;
+
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("%s: INITIATOR_MSG_IN", ahd_name(ahd));
+#endif
+ phasemis = bus_phase != P_MESGIN;
+ if (phasemis) {
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahd_lookup_phase_entry(bus_phase)
+ ->phasemsg);
+ }
+#endif
+ ahd->msgin_index = 0;
+ if (bus_phase == P_MESGOUT
+ && (ahd->send_msg_perror != 0
+ || (ahd->msgout_len != 0
+ && ahd->msgout_index == 0))) {
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ goto reswitch;
+ }
+ end_session = TRUE;
+ break;
+ }
+
+ /* Pull the byte in without acking it */
+ ahd->msgin_buf[ahd->msgin_index] = ahd_inb(ahd, SCSIBUS);
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahd->msgin_buf[ahd->msgin_index]);
+#endif
+
+ message_done = ahd_parse_msg(ahd, &devinfo);
+
+ if (message_done) {
+ /*
+ * Clear our incoming message buffer in case there
+ * is another message following this one.
+ */
+ ahd->msgin_index = 0;
+
+ /*
+ * If this message illicited a response,
+ * assert ATN so the target takes us to the
+ * message out phase.
+ */
+ if (ahd->msgout_len != 0)
+ ahd_assert_atn(ahd);
+ } else
+ ahd->msgin_index++;
+
+ if (message_done == MSGLOOP_TERMINATED) {
+ end_session = TRUE;
+ } else {
+ /* Ack the byte */
+ ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_READ);
+ }
+ break;
+ }
+ case MSG_TYPE_TARGET_MSGIN:
+ {
+ int msgdone;
+ int msgout_request;
+
+ /*
+ * By default, the message loop will continue.
+ */
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG);
+
+ if (ahd->msgout_len == 0)
+ panic("Target MSGIN with no active message");
+
+ /*
+ * If we interrupted a mesgout session, the initiator
+ * will not know this until our first REQ. So, we
+ * only honor mesgout requests after we've sent our
+ * first byte.
+ */
+ if ((ahd_inb(ahd, SCSISIGI) & ATNI) != 0
+ && ahd->msgout_index > 0)
+ msgout_request = TRUE;
+ else
+ msgout_request = FALSE;
+
+ if (msgout_request) {
+
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahd->msg_type = MSG_TYPE_TARGET_MSGOUT;
+ ahd_outb(ahd, SCSISIGO, P_MESGOUT | BSYO);
+ ahd->msgin_index = 0;
+ /* Dummy read to REQ for first byte */
+ ahd_inb(ahd, SCSIDAT);
+ ahd_outb(ahd, SXFRCTL0,
+ ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+ break;
+ }
+
+ msgdone = ahd->msgout_index == ahd->msgout_len;
+ if (msgdone) {
+ ahd_outb(ahd, SXFRCTL0,
+ ahd_inb(ahd, SXFRCTL0) & ~SPIOEN);
+ end_session = TRUE;
+ break;
+ }
+
+ /*
+ * Present the next byte on the bus.
+ */
+ ahd_outb(ahd, SXFRCTL0, ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+ ahd_outb(ahd, SCSIDAT, ahd->msgout_buf[ahd->msgout_index++]);
+ break;
+ }
+ case MSG_TYPE_TARGET_MSGOUT:
+ {
+ int lastbyte;
+ int msgdone;
+
+ /*
+ * By default, the message loop will continue.
+ */
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG);
+
+ /*
+ * The initiator signals that this is
+ * the last byte by dropping ATN.
+ */
+ lastbyte = (ahd_inb(ahd, SCSISIGI) & ATNI) == 0;
+
+ /*
+ * Read the latched byte, but turn off SPIOEN first
+ * so that we don't inadvertently cause a REQ for the
+ * next byte.
+ */
+ ahd_outb(ahd, SXFRCTL0, ahd_inb(ahd, SXFRCTL0) & ~SPIOEN);
+ ahd->msgin_buf[ahd->msgin_index] = ahd_inb(ahd, SCSIDAT);
+ msgdone = ahd_parse_msg(ahd, &devinfo);
+ if (msgdone == MSGLOOP_TERMINATED) {
+ /*
+ * The message is *really* done in that it caused
+ * us to go to bus free. The sequencer has already
+ * been reset at this point, so pull the ejection
+ * handle.
+ */
+ return;
+ }
+
+ ahd->msgin_index++;
+
+ /*
+ * XXX Read spec about initiator dropping ATN too soon
+ * and use msgdone to detect it.
+ */
+ if (msgdone == MSGLOOP_MSGCOMPLETE) {
+ ahd->msgin_index = 0;
+
+ /*
+ * If this message illicited a response, transition
+ * to the Message in phase and send it.
+ */
+ if (ahd->msgout_len != 0) {
+ ahd_outb(ahd, SCSISIGO, P_MESGIN | BSYO);
+ ahd_outb(ahd, SXFRCTL0,
+ ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+ ahd->msg_type = MSG_TYPE_TARGET_MSGIN;
+ ahd->msgin_index = 0;
+ break;
+ }
+ }
+
+ if (lastbyte)
+ end_session = TRUE;
+ else {
+ /* Ask for the next byte. */
+ ahd_outb(ahd, SXFRCTL0,
+ ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+ }
+
+ break;
+ }
+ default:
+ panic("Unknown REQINIT message type");
+ }
+
+ if (end_session) {
+ if ((ahd->msg_flags & MSG_FLAG_PACKETIZED) != 0) {
+ printf("%s: Returning to Idle Loop\n",
+ ahd_name(ahd));
+ ahd_outb(ahd, LASTPHASE, P_BUSFREE);
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
+ } else {
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, RETURN_1, EXIT_MSG_LOOP);
+ }
+ }
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message. If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type, u_int msgval, int full)
+{
+ int found;
+ u_int index;
+
+ found = FALSE;
+ index = 0;
+
+ while (index < ahd->msgout_len) {
+ if (ahd->msgout_buf[index] == MSG_EXTENDED) {
+ u_int end_index;
+
+ end_index = index + 1 + ahd->msgout_buf[index + 1];
+ if (ahd->msgout_buf[index+2] == msgval
+ && type == AHDMSG_EXT) {
+
+ if (full) {
+ if (ahd->msgout_index > end_index)
+ found = TRUE;
+ } else if (ahd->msgout_index > index)
+ found = TRUE;
+ }
+ index = end_index;
+ } else if (ahd->msgout_buf[index] >= MSG_SIMPLE_TASK
+ && ahd->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+ /* Skip tag type and tag id or residue param*/
+ index += 2;
+ } else {
+ /* Single byte message */
+ if (type == AHDMSG_1B
+ && ahd->msgout_buf[index] == msgval
+ && ahd->msgout_index > index)
+ found = TRUE;
+ index++;
+ }
+
+ if (found)
+ break;
+ }
+ return (found);
+}
+
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
+static int
+ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ int reject;
+ int done;
+ int response;
+
+ done = MSGLOOP_IN_PROG;
+ response = FALSE;
+ reject = FALSE;
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ /*
+ * Parse as much of the message as is availible,
+ * rejecting it if we don't support it. When
+ * the entire message is availible and has been
+ * handled, return MSGLOOP_MSGCOMPLETE, indicating
+ * that we have parsed an entire message.
+ *
+ * In the case of extended messages, we accept the length
+ * byte outright and perform more checking once we know the
+ * extended message type.
+ */
+ switch (ahd->msgin_buf[0]) {
+ case MSG_DISCONNECT:
+ case MSG_SAVEDATAPOINTER:
+ case MSG_CMDCOMPLETE:
+ case MSG_RESTOREPOINTERS:
+ case MSG_IGN_WIDE_RESIDUE:
+ /*
+ * End our message loop as these are messages
+ * the sequencer handles on its own.
+ */
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MSG_MESSAGE_REJECT:
+ response = ahd_handle_msg_reject(ahd, devinfo);
+ /* FALLTHROUGH */
+ case MSG_NOOP:
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ case MSG_EXTENDED:
+ {
+ /* Wait for enough of the message to begin validation */
+ if (ahd->msgin_index < 2)
+ break;
+ switch (ahd->msgin_buf[2]) {
+ case MSG_EXT_SDTR:
+ {
+ u_int period;
+ u_int ppr_options;
+ u_int offset;
+ u_int saved_offset;
+
+ if (ahd->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have both args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_SDTR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahd->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+ break;
+
+ period = ahd->msgin_buf[3];
+ ppr_options = 0;
+ saved_offset = offset = ahd->msgin_buf[4];
+ ahd_devlimited_syncrate(ahd, tinfo, &period,
+ &ppr_options, devinfo->role);
+ ahd_validate_offset(ahd, tinfo, period, &offset,
+ tinfo->curr.width, devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received "
+ "SDTR period %x, offset %x\n\t"
+ "Filtered to period %x, offset %x\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ ahd->msgin_buf[3], saved_offset,
+ period, offset);
+ }
+ ahd_set_syncrate(ahd, devinfo, period,
+ offset, ppr_options,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+
+ /*
+ * See if we initiated Sync Negotiation
+ * and didn't have to fall down to async
+ * transfers.
+ */
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+ /* We started it */
+ if (saved_offset != offset) {
+ /* Went too low - force async */
+ reject = TRUE;
+ }
+ } else {
+ /*
+ * Send our own SDTR in reply
+ */
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated SDTR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_construct_sdtr(ahd, devinfo,
+ period, offset);
+ ahd->msgout_index = 0;
+ response = TRUE;
+ }
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ case MSG_EXT_WDTR:
+ {
+ u_int bus_width;
+ u_int saved_width;
+ u_int sending_reply;
+
+ sending_reply = FALSE;
+ if (ahd->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have our arg before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_WDTR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahd->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+ break;
+
+ bus_width = ahd->msgin_buf[3];
+ saved_width = bus_width;
+ ahd_validate_width(ahd, tinfo, &bus_width,
+ devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received WDTR "
+ "%x filtered to %x\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, bus_width);
+ }
+
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+ /*
+ * Don't send a WDTR back to the
+ * target, since we asked first.
+ * If the width went higher than our
+ * request, reject it.
+ */
+ if (saved_width > bus_width) {
+ reject = TRUE;
+ printf("(%s:%c:%d:%d): requested %dBit "
+ "transfers. Rejecting...\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ 8 * (0x01 << bus_width));
+ bus_width = 0;
+ }
+ } else {
+ /*
+ * Send our own WDTR in reply
+ */
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated WDTR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_construct_wdtr(ahd, devinfo, bus_width);
+ ahd->msgout_index = 0;
+ response = TRUE;
+ sending_reply = TRUE;
+ }
+ ahd_set_width(ahd, devinfo, bus_width,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ /* After a wide message, we are async */
+ ahd_set_syncrate(ahd, devinfo, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHD_TRANS_ACTIVE, /*paused*/TRUE);
+ if (sending_reply == FALSE && reject == FALSE) {
+
+ if (tinfo->goal.period) {
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_build_transfer_msg(ahd, devinfo);
+ ahd->msgout_index = 0;
+ response = TRUE;
+ }
+ }
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ case MSG_EXT_PPR:
+ {
+ u_int period;
+ u_int offset;
+ u_int bus_width;
+ u_int ppr_options;
+ u_int saved_width;
+ u_int saved_offset;
+ u_int saved_ppr_options;
+
+ if (ahd->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have all args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_PPR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahd->msgin_index < (MSG_EXT_PPR_LEN + 1))
+ break;
+
+ period = ahd->msgin_buf[3];
+ offset = ahd->msgin_buf[5];
+ bus_width = ahd->msgin_buf[6];
+ saved_width = bus_width;
+ ppr_options = ahd->msgin_buf[7];
+ /*
+ * According to the spec, a DT only
+ * period factor with no DT option
+ * set implies async.
+ */
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && period <= 9)
+ offset = 0;
+ saved_ppr_options = ppr_options;
+ saved_offset = offset;
+
+ /*
+ * Transfer options are only available if we
+ * are negotiating wide.
+ */
+ if (bus_width == 0)
+ ppr_options &= MSG_EXT_PPR_QAS_REQ;
+
+ ahd_validate_width(ahd, tinfo, &bus_width,
+ devinfo->role);
+ ahd_devlimited_syncrate(ahd, tinfo, &period,
+ &ppr_options, devinfo->role);
+ ahd_validate_offset(ahd, tinfo, period, &offset,
+ bus_width, devinfo->role);
+
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, TRUE)) {
+ /*
+ * If we are unable to do any of the
+ * requested options (we went too low),
+ * then we'll have to reject the message.
+ */
+ if (saved_width > bus_width
+ || saved_offset != offset
+ || saved_ppr_options != ppr_options) {
+ reject = TRUE;
+ period = 0;
+ offset = 0;
+ bus_width = 0;
+ ppr_options = 0;
+ }
+ } else {
+ if (devinfo->role != ROLE_TARGET)
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated PPR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ else
+ printf("(%s:%c:%d:%d): Initiator "
+ "Initiated PPR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_construct_ppr(ahd, devinfo, period, offset,
+ bus_width, ppr_options);
+ ahd->msgout_index = 0;
+ response = TRUE;
+ }
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received PPR width %x, "
+ "period %x, offset %x,options %x\n"
+ "\tFiltered to width %x, period %x, "
+ "offset %x, options %x\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, ahd->msgin_buf[3],
+ saved_offset, saved_ppr_options,
+ bus_width, period, offset, ppr_options);
+ }
+ ahd_set_width(ahd, devinfo, bus_width,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ saved_ppr_options = tinfo->curr.ppr_options;
+ if ((saved_ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("PPR with IU_REQ outstanding\n");
+#endif
+ ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE;
+ }
+ if ((saved_ppr_options & MSG_EXT_PPR_IU_REQ)
+ != (ppr_options & MSG_EXT_PPR_IU_REQ)) {
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Expecting IU Change busfree\n");
+#endif
+ ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE
+ | MSG_FLAG_IU_REQ_CHANGED;
+ }
+
+ ahd_set_syncrate(ahd, devinfo, period,
+ offset, ppr_options,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ default:
+ /* Unknown extended message. Reject it. */
+ reject = TRUE;
+ break;
+ }
+ break;
+ }
+#ifdef AHD_TARGET_MODE
+ case MSG_BUS_DEV_RESET:
+ ahd_handle_devreset(ahd, devinfo,
+ CAM_BDR_SENT,
+ "Bus Device Reset Received",
+ /*verbose_level*/0);
+ ahd_restart(ahd);
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MSG_ABORT_TAG:
+ case MSG_ABORT:
+ case MSG_CLEAR_QUEUE:
+ {
+ int tag;
+
+ /* Target mode messages */
+ if (devinfo->role != ROLE_TARGET) {
+ reject = TRUE;
+ break;
+ }
+ tag = SCB_LIST_NULL;
+ if (ahd->msgin_buf[0] == MSG_ABORT_TAG)
+ tag = ahd_inb(ahd, INITIATOR_TAG);
+ ahd_abort_scbs(ahd, devinfo->target, devinfo->channel,
+ devinfo->lun, tag, ROLE_TARGET,
+ CAM_REQ_ABORTED);
+
+ tstate = ahd->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ struct ahd_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[devinfo->lun];
+ if (lstate != NULL) {
+ ahd_queue_lstate_event(ahd, lstate,
+ devinfo->our_scsiid,
+ ahd->msgin_buf[0],
+ /*arg*/tag);
+ ahd_send_lstate_events(ahd, lstate);
+ }
+ }
+ ahd_restart(ahd);
+ done = MSGLOOP_TERMINATED;
+ break;
+ }
+#endif
+ case MSG_QAS_REQUEST:
+ printf("%s: QAS request. SCSISIGI == 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, SCSISIGI));
+ /* FALLTHROUGH */
+ case MSG_TERM_IO_PROC:
+ default:
+ reject = TRUE;
+ break;
+ }
+
+ if (reject) {
+ /*
+ * Setup to reject the message.
+ */
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 1;
+ ahd->msgout_buf[0] = MSG_MESSAGE_REJECT;
+ done = MSGLOOP_MSGCOMPLETE;
+ response = TRUE;
+ }
+
+ if (done != MSGLOOP_IN_PROG && !response)
+ /* Clear the outgoing message buffer */
+ ahd->msgout_len = 0;
+
+ return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ /*
+ * What we care about here is if we had an
+ * outstanding SDTR or WDTR message for this
+ * target. If we did, this is a signal that
+ * the target is refusing negotiation.
+ */
+ struct scb *scb;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int scb_index;
+ u_int last_msg;
+ int response = 0;
+
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ /* Might be necessary */
+ last_msg = ahd_inb(ahd, LAST_MSG);
+
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+ /*
+ * Target does not support the PPR message.
+ * Attempt to negotiate SPI-2 style.
+ */
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): PPR Rejected. "
+ "Trying WDTR/SDTR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ tinfo->goal.ppr_options = 0;
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_build_transfer_msg(ahd, devinfo);
+ ahd->msgout_index = 0;
+ response = 1;
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+ /* note 8bit xfers */
+ printf("(%s:%c:%d:%d): refuses WIDE negotiation. Using "
+ "8bit transfers\n", ahd_name(ahd),
+ devinfo->channel, devinfo->target, devinfo->lun);
+ ahd_set_width(ahd, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ /*
+ * No need to clear the sync rate. If the target
+ * did not accept the command, our syncrate is
+ * unaffected. If the target started the negotiation,
+ * but rejected our response, we already cleared the
+ * sync rate before sending our WDTR.
+ */
+ if (tinfo->goal.period) {
+
+ /* Start the sync negotiation */
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_build_transfer_msg(ahd, devinfo);
+ ahd->msgout_index = 0;
+ response = 1;
+ }
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+ /* note asynch xfers and clear flag */
+ ahd_set_syncrate(ahd, devinfo, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ printf("(%s:%c:%d:%d): refuses synchronous negotiation. "
+ "Using asynchronous transfers\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ } else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+ int tag_type;
+ int mask;
+
+ tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+ if (tag_type == MSG_SIMPLE_TASK) {
+ printf("(%s:%c:%d:%d): refuses tagged commands. "
+ "Performing non-tagged I/O\n", ahd_name(ahd),
+ devinfo->channel, devinfo->target, devinfo->lun);
+ ahd_set_tags(ahd, devinfo, AHD_QUEUE_NONE);
+ mask = ~0x23;
+ } else {
+ printf("(%s:%c:%d:%d): refuses %s tagged commands. "
+ "Performing simple queue tagged I/O only\n",
+ ahd_name(ahd), devinfo->channel, devinfo->target,
+ devinfo->lun, tag_type == MSG_ORDERED_TASK
+ ? "ordered" : "head of queue");
+ ahd_set_tags(ahd, devinfo, AHD_QUEUE_BASIC);
+ mask = ~0x03;
+ }
+
+ /*
+ * Resend the identify for this CCB as the target
+ * may believe that the selection is invalid otherwise.
+ */
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb_scbram(ahd, SCB_CONTROL) & mask);
+ scb->hscb->control &= mask;
+ ahd_set_transaction_tag(scb, /*enabled*/FALSE,
+ /*type*/MSG_SIMPLE_TASK);
+ ahd_outb(ahd, MSG_OUT, MSG_IDENTIFYFLAG);
+ ahd_assert_atn(ahd);
+ ahd_busy_tcl(ahd, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+ SCB_GET_TAG(scb));
+
+ /*
+ * Requeue all tagged commands for this target
+ * currently in our posession so they can be
+ * converted to untagged commands.
+ */
+ ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ } else {
+ /*
+ * Otherwise, we ignore it.
+ */
+ printf("%s:%c:%d: Message reject for %x -- ignored\n",
+ ahd_name(ahd), devinfo->channel, devinfo->target,
+ last_msg);
+ }
+ return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahd_handle_ign_wide_residue(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ u_int scb_index;
+ struct scb *scb;
+
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ /*
+ * XXX Actually check data direction in the sequencer?
+ * Perhaps add datadir to some spare bits in the hscb?
+ */
+ if ((ahd_inb(ahd, SEQ_FLAGS) & DPHASE) == 0
+ || ahd_get_transfer_dir(scb) != CAM_DIR_IN) {
+ /*
+ * Ignore the message if we haven't
+ * seen an appropriate data phase yet.
+ */
+ } else {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing. Otherwise, subtract a byte
+ * and update the residual count accordingly.
+ */
+ uint32_t sgptr;
+
+ sgptr = ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR);
+ if ((sgptr & SG_LIST_NULL) != 0
+ && ahd_inb(ahd, DATA_COUNT_ODD) == 1) {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing.
+ */
+ } else {
+ uint32_t data_cnt;
+ uint64_t data_addr;
+ uint32_t sglen;
+
+ /* Pull in the rest of the sgptr */
+ sgptr |=
+ (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 3) << 24)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 2) << 16)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 1) << 8);
+ sgptr &= SG_PTR_MASK;
+ data_cnt =
+ (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+3) << 24)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+2) << 16)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+1) << 8)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT));
+
+ data_addr = (((uint64_t)ahd_inb(ahd, SHADDR + 7)) << 56)
+ | (((uint64_t)ahd_inb(ahd, SHADDR + 6)) << 48)
+ | (((uint64_t)ahd_inb(ahd, SHADDR + 5)) << 40)
+ | (((uint64_t)ahd_inb(ahd, SHADDR + 4)) << 32)
+ | (ahd_inb(ahd, SHADDR + 3) << 24)
+ | (ahd_inb(ahd, SHADDR + 2) << 16)
+ | (ahd_inb(ahd, SHADDR + 1) << 8)
+ | (ahd_inb(ahd, SHADDR));
+
+ data_cnt += 1;
+ data_addr -= 1;
+
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+ /*
+ * The residual sg ptr points to the next S/G
+ * to load so we must go back one.
+ */
+ sg--;
+ sglen = ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+ if (sg != scb->sg_list
+ && sglen < (data_cnt & AHD_SG_LEN_MASK)) {
+
+ sg--;
+ sglen = ahd_le32toh(sg->len);
+ /*
+ * Preserve High Address and SG_LIST
+ * bits while setting the count to 1.
+ */
+ data_cnt = 1|(sglen&(~AHD_SG_LEN_MASK));
+ data_addr = ahd_le64toh(sg->addr)
+ + (sglen & AHD_SG_LEN_MASK)
+ - 1;
+
+ /*
+ * Increment sg so it points to the
+ * "next" sg.
+ */
+ sg++;
+ sgptr = ahd_sg_virt_to_bus(ahd, scb,
+ sg);
+ }
+ } else {
+ struct ahd_dma_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+ /*
+ * The residual sg ptr points to the next S/G
+ * to load so we must go back one.
+ */
+ sg--;
+ sglen = ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+ if (sg != scb->sg_list
+ && sglen < (data_cnt & AHD_SG_LEN_MASK)) {
+
+ sg--;
+ sglen = ahd_le32toh(sg->len);
+ /*
+ * Preserve High Address and SG_LIST
+ * bits while setting the count to 1.
+ */
+ data_cnt = 1|(sglen&(~AHD_SG_LEN_MASK));
+ data_addr = ahd_le32toh(sg->addr)
+ + (sglen & AHD_SG_LEN_MASK)
+ - 1;
+
+ /*
+ * Increment sg so it points to the
+ * "next" sg.
+ */
+ sg++;
+ sgptr = ahd_sg_virt_to_bus(ahd, scb,
+ sg);
+ }
+ }
+ ahd_outb(ahd, SCB_RESIDUAL_SGPTR + 3, sgptr >> 24);
+ ahd_outb(ahd, SCB_RESIDUAL_SGPTR + 2, sgptr >> 16);
+ ahd_outb(ahd, SCB_RESIDUAL_SGPTR + 1, sgptr >> 8);
+ ahd_outb(ahd, SCB_RESIDUAL_SGPTR, sgptr);
+
+ ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 3, data_cnt >> 24);
+ ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 2, data_cnt >> 16);
+ ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 1, data_cnt >> 8);
+ ahd_outb(ahd, SCB_RESIDUAL_DATACNT, data_cnt);
+
+ /*
+ * The FIFO's pointers will be updated if/when the
+ * sequencer re-enters a data phase.
+ */
+ }
+ }
+}
+
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahd_reinitialize_dataptrs(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ ahd_mode_state saved_modes;
+ u_int scb_index;
+ u_int wait;
+ uint32_t sgptr;
+ uint32_t resid;
+ uint64_t dataptr;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK,
+ AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK);
+
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+
+ /*
+ * Release and reacquire the FIFO so we
+ * have a clean slate.
+ */
+ ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
+ wait = 1000;
+ do {
+ ahd_delay(100);
+ } while (--wait && !(ahd_inb(ahd, MDFFSTAT) & FIFOFREE));
+ if (wait == 0) {
+ ahd_print_path(ahd, scb);
+ printf("ahd_reinitialize_dataptrs: Forcing FIFO free.\n");
+ ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT);
+ }
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, DFFSTAT,
+ ahd_inb(ahd, DFFSTAT) | (saved_modes == 0x11 ? CURRFIFO : 0));
+
+ /*
+ * Determine initial values for data_addr and data_cnt
+ * for resuming the data phase.
+ */
+ sgptr = (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 3) << 24)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 2) << 16)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 1) << 8)
+ | ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR);
+ sgptr &= SG_PTR_MASK;
+
+ resid = (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT + 2) << 16)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT + 1) << 8)
+ | ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT);
+
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ dataptr = ahd_le64toh(sg->addr)
+ + (ahd_le32toh(sg->len) & AHD_SG_LEN_MASK)
+ - resid;
+ ahd_outb(ahd, HADDR + 7, dataptr >> 56);
+ ahd_outb(ahd, HADDR + 6, dataptr >> 48);
+ ahd_outb(ahd, HADDR + 5, dataptr >> 40);
+ ahd_outb(ahd, HADDR + 4, dataptr >> 32);
+ } else {
+ struct ahd_dma_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ dataptr = ahd_le32toh(sg->addr)
+ + (ahd_le32toh(sg->len) & AHD_SG_LEN_MASK)
+ - resid;
+ ahd_outb(ahd, HADDR + 4,
+ (ahd_le32toh(sg->len) & ~AHD_SG_LEN_MASK) >> 24);
+ }
+ ahd_outb(ahd, HADDR + 3, dataptr >> 24);
+ ahd_outb(ahd, HADDR + 2, dataptr >> 16);
+ ahd_outb(ahd, HADDR + 1, dataptr >> 8);
+ ahd_outb(ahd, HADDR, dataptr);
+ ahd_outb(ahd, HCNT + 2, resid >> 16);
+ ahd_outb(ahd, HCNT + 1, resid >> 8);
+ ahd_outb(ahd, HCNT, resid);
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ cam_status status, char *message, int verbose_level)
+{
+#ifdef AHD_TARGET_MODE
+ struct ahd_tmode_tstate* tstate;
+ u_int lun;
+#endif
+ int found;
+
+ found = ahd_abort_scbs(ahd, devinfo->target, devinfo->channel,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
+ status);
+
+#ifdef AHD_TARGET_MODE
+ /*
+ * Send an immediate notify ccb to all target mord peripheral
+ * drivers affected by this action.
+ */
+ tstate = ahd->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+ struct ahd_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[lun];
+ if (lstate == NULL)
+ continue;
+
+ ahd_queue_lstate_event(ahd, lstate, devinfo->our_scsiid,
+ MSG_BUS_DEV_RESET, /*arg*/0);
+ ahd_send_lstate_events(ahd, lstate);
+ }
+ }
+#endif
+
+ /*
+ * Go back to async/narrow transfers and renegotiate.
+ */
+ ahd_set_width(ahd, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR, /*paused*/TRUE);
+ ahd_set_syncrate(ahd, devinfo, /*period*/0, /*offset*/0,
+ /*ppr_options*/0, AHD_TRANS_CUR, /*paused*/TRUE);
+
+ ahd_send_async(ahd, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
+
+ if (message != NULL
+ && (verbose_level <= bootverbose))
+ printf("%s: %s on %c:%d. %d SCBs aborted\n", ahd_name(ahd),
+ message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHD_TARGET_MODE
+static void
+ahd_setup_target_msgin(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct scb *scb)
+{
+
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+
+ if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+ ahd_build_transfer_msg(ahd, devinfo);
+ else
+ panic("ahd_intr: AWAITING target message with no message");
+
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+static bus_size_t
+ahd_sglist_size(struct ahd_softc *ahd)
+{
+ bus_size_t list_size;
+
+ list_size = sizeof(struct ahd_dma_seg) * AHD_NSEG;
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+ list_size = sizeof(struct ahd_dma64_seg) * AHD_NSEG;
+ return (list_size);
+}
+
+/*
+ * Calculate the optimum S/G List allocation size. S/G elements used
+ * for a given transaction must be physically contiguous. Assume the
+ * OS will allocate full pages to us, so it doesn't make sense to request
+ * less than a page.
+ */
+static bus_size_t
+ahd_sglist_allocsize(struct ahd_softc *ahd)
+{
+ bus_size_t sg_list_increment;
+ bus_size_t sg_list_size;
+ bus_size_t max_list_size;
+ bus_size_t best_list_size;
+
+ /* Start out with the minimum required for AHD_NSEG. */
+ sg_list_increment = ahd_sglist_size(ahd);
+ sg_list_size = sg_list_increment;
+
+ /* Get us as close as possible to a page in size. */
+ while ((sg_list_size + sg_list_increment) <= PAGE_SIZE)
+ sg_list_size += sg_list_increment;
+
+ /*
+ * Try to reduce the amount of wastage by allocating
+ * multiple pages.
+ */
+ best_list_size = sg_list_size;
+ max_list_size = roundup(sg_list_increment, PAGE_SIZE);
+ if (max_list_size < 4 * PAGE_SIZE)
+ max_list_size = 4 * PAGE_SIZE;
+ if (max_list_size > (AHD_SCB_MAX_ALLOC * sg_list_increment))
+ max_list_size = (AHD_SCB_MAX_ALLOC * sg_list_increment);
+ while ((sg_list_size + sg_list_increment) <= max_list_size
+ && (sg_list_size % PAGE_SIZE) != 0) {
+ bus_size_t new_mod;
+ bus_size_t best_mod;
+
+ sg_list_size += sg_list_increment;
+ new_mod = sg_list_size % PAGE_SIZE;
+ best_mod = best_list_size % PAGE_SIZE;
+ if (new_mod > best_mod || new_mod == 0) {
+ best_list_size = sg_list_size;
+ }
+ }
+ return (best_list_size);
+}
+
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahd_softc *
+ahd_alloc(void *platform_arg, char *name)
+{
+ struct ahd_softc *ahd;
+
+#ifndef __FreeBSD__
+ ahd = malloc(sizeof(*ahd), M_DEVBUF, M_NOWAIT);
+ if (!ahd) {
+ printf("aic7xxx: cannot malloc softc!\n");
+ free(name, M_DEVBUF);
+ return NULL;
+ }
+#else
+ ahd = device_get_softc((device_t)platform_arg);
+#endif
+ memset(ahd, 0, sizeof(*ahd));
+ ahd->seep_config = malloc(sizeof(*ahd->seep_config),
+ M_DEVBUF, M_NOWAIT);
+ if (ahd->seep_config == NULL) {
+#ifndef __FreeBSD__
+ free(ahd, M_DEVBUF);
+#endif
+ free(name, M_DEVBUF);
+ return (NULL);
+ }
+ LIST_INIT(&ahd->pending_scbs);
+ /* We don't know our unit number until the OSM sets it */
+ ahd->name = name;
+ ahd->unit = -1;
+ ahd->description = NULL;
+ ahd->bus_description = NULL;
+ ahd->channel = 'A';
+ ahd->chip = AHD_NONE;
+ ahd->features = AHD_FENONE;
+ ahd->bugs = AHD_BUGNONE;
+ ahd->flags = AHD_SPCHK_ENB_A|AHD_RESET_BUS_A|AHD_TERM_ENB_A
+ | AHD_EXTENDED_TRANS_A|AHD_STPWLEVEL_A;
+ ahd_timer_init(&ahd->reset_timer);
+
+ if (ahd_platform_alloc(ahd, platform_arg) != 0) {
+ ahd_free(ahd);
+ ahd = NULL;
+ }
+ return (ahd);
+}
+
+int
+ahd_softc_init(struct ahd_softc *ahd)
+{
+
+ ahd->unpause = 0;
+ ahd->pause = PAUSE;
+ return (0);
+}
+
+void
+ahd_softc_insert(struct ahd_softc *ahd)
+{
+ struct ahd_softc *list_ahd;
+
+#if AHD_PCI_CONFIG > 0
+ /*
+ * Second Function PCI devices need to inherit some
+ * settings from function 0.
+ */
+ if ((ahd->features & AHD_MULTI_FUNC) != 0) {
+ TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+ ahd_dev_softc_t list_pci;
+ ahd_dev_softc_t pci;
+
+ list_pci = list_ahd->dev_softc;
+ pci = ahd->dev_softc;
+ if (ahd_get_pci_slot(list_pci) == ahd_get_pci_slot(pci)
+ && ahd_get_pci_bus(list_pci) == ahd_get_pci_bus(pci)) {
+ struct ahd_softc *master;
+ struct ahd_softc *slave;
+
+ if (ahd_get_pci_function(list_pci) == 0) {
+ master = list_ahd;
+ slave = ahd;
+ } else {
+ master = ahd;
+ slave = list_ahd;
+ }
+ slave->flags &= ~AHD_BIOS_ENABLED;
+ slave->flags |=
+ master->flags & AHD_BIOS_ENABLED;
+ slave->flags &= ~AHD_PRIMARY_CHANNEL;
+ slave->flags |=
+ master->flags & AHD_PRIMARY_CHANNEL;
+ break;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Insertion sort into our list of softcs.
+ */
+ list_ahd = TAILQ_FIRST(&ahd_tailq);
+ while (list_ahd != NULL
+ && ahd_softc_comp(list_ahd, ahd) <= 0)
+ list_ahd = TAILQ_NEXT(list_ahd, links);
+ if (list_ahd != NULL)
+ TAILQ_INSERT_BEFORE(list_ahd, ahd, links);
+ else
+ TAILQ_INSERT_TAIL(&ahd_tailq, ahd, links);
+ ahd->init_level++;
+}
+
+/*
+ * Verify that the passed in softc pointer is for a
+ * controller that is still configured.
+ */
+struct ahd_softc *
+ahd_find_softc(struct ahd_softc *ahd)
+{
+ struct ahd_softc *list_ahd;
+
+ TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+ if (list_ahd == ahd)
+ return (ahd);
+ }
+ return (NULL);
+}
+
+void
+ahd_set_unit(struct ahd_softc *ahd, int unit)
+{
+ ahd->unit = unit;
+}
+
+void
+ahd_set_name(struct ahd_softc *ahd, char *name)
+{
+ if (ahd->name != NULL)
+ free(ahd->name, M_DEVBUF);
+ ahd->name = name;
+}
+
+void
+ahd_free(struct ahd_softc *ahd)
+{
+ int i;
+
+ ahd_fini_scbdata(ahd);
+ switch (ahd->init_level) {
+ default:
+ case 5:
+ ahd_shutdown(ahd);
+ TAILQ_REMOVE(&ahd_tailq, ahd, links);
+ /* FALLTHROUGH */
+ case 4:
+ ahd_dmamap_unload(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap);
+ /* FALLTHROUGH */
+ case 3:
+ ahd_dmamem_free(ahd, ahd->shared_data_dmat, ahd->qoutfifo,
+ ahd->shared_data_dmamap);
+ ahd_dmamap_destroy(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap);
+ /* FALLTHROUGH */
+ case 2:
+ ahd_dma_tag_destroy(ahd, ahd->shared_data_dmat);
+ case 1:
+#ifndef __linux__
+ ahd_dma_tag_destroy(ahd, ahd->buffer_dmat);
+#endif
+ break;
+ case 0:
+ break;
+ }
+
+#ifndef __linux__
+ ahd_dma_tag_destroy(ahd, ahd->parent_dmat);
+#endif
+ ahd_platform_free(ahd);
+ for (i = 0; i < AHD_NUM_TARGETS; i++) {
+ struct ahd_tmode_tstate *tstate;
+
+ tstate = ahd->enabled_targets[i];
+ if (tstate != NULL) {
+#if AHD_TARGET_MODE
+ int j;
+
+ for (j = 0; j < AHD_NUM_LUNS; j++) {
+ struct ahd_tmode_lstate *lstate;
+
+ lstate = tstate->enabled_luns[j];
+ if (lstate != NULL) {
+ xpt_free_path(lstate->path);
+ free(lstate, M_DEVBUF);
+ }
+ }
+#endif
+ free(tstate, M_DEVBUF);
+ }
+ }
+#if AHD_TARGET_MODE
+ if (ahd->black_hole != NULL) {
+ xpt_free_path(ahd->black_hole->path);
+ free(ahd->black_hole, M_DEVBUF);
+ }
+#endif
+ if (ahd->name != NULL)
+ free(ahd->name, M_DEVBUF);
+ if (ahd->seep_config != NULL)
+ free(ahd->seep_config, M_DEVBUF);
+#ifndef __FreeBSD__
+ free(ahd, M_DEVBUF);
+#endif
+ return;
+}
+
+void
+ahd_shutdown(void *arg)
+{
+ struct ahd_softc *ahd;
+
+ ahd = (struct ahd_softc *)arg;
+
+ /* This will reset most registers to 0, but not all */
+ ahd_reset(ahd);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only available just after a reset.
+ */
+int
+ahd_reset(struct ahd_softc *ahd)
+{
+ u_int sxfrctl1;
+ int wait;
+ uint32_t cmd;
+
+ /*
+ * Preserve the value of the SXFRCTL1 register for all channels.
+ * It contains settings that affect termination and we don't want
+ * to disturb the integrity of the bus.
+ */
+ ahd_pause(ahd);
+ sxfrctl1 = ahd_inb(ahd, SXFRCTL1);
+
+ cmd = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2);
+ if ((ahd->bugs & AHD_PCIX_CHIPRST_BUG) != 0) {
+ uint32_t mod_cmd;
+
+ /*
+ * A4 Razor #632
+ * During the assertion of CHIPRST, the chip
+ * does not disable its parity logic prior to
+ * the start of the reset. This may cause a
+ * parity error to be detected and thus a
+ * spurious SERR or PERR assertion. Disble
+ * PERR and SERR responses during the CHIPRST.
+ */
+ mod_cmd = cmd & ~(PCIM_CMD_PERRESPEN|PCIM_CMD_SERRESPEN);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+ mod_cmd, /*bytes*/2);
+ }
+ ahd_outb(ahd, HCNTRL, CHIPRST | ahd->pause);
+
+ /*
+ * Ensure that the reset has finished. We delay 1000us
+ * prior to reading the register to make sure the chip
+ * has sufficiently completed its reset to handle register
+ * accesses.
+ */
+ wait = 1000;
+ do {
+ ahd_delay(1000);
+ } while (--wait && !(ahd_inb(ahd, HCNTRL) & CHIPRSTACK));
+
+ if (wait == 0) {
+ printf("%s: WARNING - Failed chip reset! "
+ "Trying to initialize anyway.\n", ahd_name(ahd));
+ }
+ ahd_outb(ahd, HCNTRL, ahd->pause);
+
+ if ((ahd->bugs & AHD_PCIX_CHIPRST_BUG) != 0) {
+ /*
+ * Clear any latched PCI error status and restore
+ * previous SERR and PERR response enables.
+ */
+ ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+ 0xFF, /*bytes*/1);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+ cmd, /*bytes*/2);
+ }
+ /* After a reset, we know the state of the mode register. */
+ ahd_known_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ /* Determine chip configuration */
+ ahd->features &= ~AHD_WIDE;
+ if ((ahd_inb(ahd, SBLKCTL) & SELWIDE) != 0)
+ ahd->features |= AHD_WIDE;
+
+ /*
+ * Restore SXFRCTL1.
+ *
+ * We must always initialize STPWEN to 1 before we
+ * restore the saved values. STPWEN is initialized
+ * to a tri-state condition which can only be cleared
+ * by turning it on.
+ */
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1|STPWEN);
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1);
+
+ /*
+ * If a recovery action has forced a chip reset,
+ * re-initialize the chip to our likeing.
+ */
+ if (ahd->init_level > 0)
+ ahd_chip_init(ahd);
+
+ return (0);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahd_probe_scbs(struct ahd_softc *ahd) {
+ int i;
+
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ for (i = 0; i < AHD_SCB_MAX; i++) {
+ int j;
+
+ ahd_set_scbptr(ahd, i);
+ ahd_outw(ahd, SCB_BASE, i);
+ for (j = 2; j < 64; j++)
+ ahd_outb(ahd, SCB_BASE+j, 0);
+ /* Start out life as unallocated (needing an abort) */
+ ahd_outb(ahd, SCB_CONTROL, MK_MESSAGE);
+ if (ahd_inw_scbram(ahd, SCB_BASE) != i)
+ break;
+ ahd_set_scbptr(ahd, 0);
+ if (ahd_inw_scbram(ahd, SCB_BASE) != 0)
+ break;
+ }
+ return (i);
+}
+
+static void
+ahd_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ bus_addr_t *baddr;
+
+ baddr = (bus_addr_t *)arg;
+ *baddr = segs->ds_addr;
+}
+
+static void
+ahd_initialize_hscbs(struct ahd_softc *ahd)
+{
+ int i;
+
+ for (i = 0; i < ahd->scb_data.maxhscbs; i++) {
+ ahd_set_scbptr(ahd, i);
+
+ /* Clear the control byte. */
+ ahd_outb(ahd, SCB_CONTROL, 0);
+
+ /* Set the next pointer */
+ ahd_outw(ahd, SCB_NEXT, SCB_LIST_NULL);
+ }
+}
+
+static int
+ahd_init_scbdata(struct ahd_softc *ahd)
+{
+ struct scb_data *scb_data;
+
+ scb_data = &ahd->scb_data;
+ SLIST_INIT(&scb_data->free_scbs);
+ SLIST_INIT(&scb_data->hscb_maps);
+ SLIST_INIT(&scb_data->sg_maps);
+ SLIST_INIT(&scb_data->sense_maps);
+
+ /* Determine the number of hardware SCBs and initialize them */
+ scb_data->maxhscbs = ahd_probe_scbs(ahd);
+ if (scb_data->maxhscbs == 0) {
+ printf("%s: No SCB space found\n", ahd_name(ahd));
+ return (ENXIO);
+ }
+
+ ahd_initialize_hscbs(ahd);
+
+ /*
+ * Create our DMA tags. These tags define the kinds of device
+ * accessible memory allocations and memory mappings we will
+ * need to perform during normal operation.
+ *
+ * Unless we need to further restrict the allocation, we rely
+ * on the restrictions of the parent dmat, hence the common
+ * use of MAXADDR and MAXSIZE.
+ */
+
+ /* DMA tag for our hardware scb structures */
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ PAGE_SIZE, /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->hscb_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* DMA tag for our S/G structures. */
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ ahd_sglist_allocsize(ahd), /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->sg_dmat) != 0) {
+ goto error_exit;
+ }
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MEMORY) != 0)
+ printf("%s: ahd_sglist_allocsize = 0x%x\n", ahd_name(ahd),
+ ahd_sglist_allocsize(ahd));
+#endif
+
+ scb_data->init_level++;
+
+ /* DMA tag for our sense buffers. We allocate in page sized chunks */
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ PAGE_SIZE, /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->sense_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* Perform initial CCB allocation */
+ ahd_alloc_scbs(ahd);
+
+ if (scb_data->numscbs == 0) {
+ printf("%s: ahd_init_scbdata - "
+ "Unable to allocate initial scbs\n",
+ ahd_name(ahd));
+ goto error_exit;
+ }
+
+ /*
+ * Reserve an SCB as the initial "next SCB" to be
+ * queued to the controller.
+ */
+ ahd->next_queued_scb = ahd_get_scb(ahd);
+
+ /*
+ * Note that we were successfull
+ */
+ return (0);
+
+error_exit:
+
+ return (ENOMEM);
+}
+
+static void
+ahd_fini_scbdata(struct ahd_softc *ahd)
+{
+ struct scb_data *scb_data;
+
+ scb_data = &ahd->scb_data;
+ if (scb_data == NULL)
+ return;
+
+ switch (scb_data->init_level) {
+ default:
+ case 7:
+ {
+ struct map_node *sns_map;
+
+ while ((sns_map = SLIST_FIRST(&scb_data->sense_maps)) != NULL) {
+ SLIST_REMOVE_HEAD(&scb_data->sense_maps, links);
+ ahd_dmamap_unload(ahd, scb_data->sense_dmat,
+ sns_map->dmamap);
+ ahd_dmamem_free(ahd, scb_data->sense_dmat,
+ sns_map->vaddr, sns_map->dmamap);
+ free(sns_map, M_DEVBUF);
+ }
+ ahd_dma_tag_destroy(ahd, scb_data->sense_dmat);
+ /* FALLTHROUGH */
+ }
+ case 6:
+ {
+ struct map_node *sg_map;
+
+ while ((sg_map = SLIST_FIRST(&scb_data->sg_maps)) != NULL) {
+ SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+ ahd_dmamap_unload(ahd, scb_data->sg_dmat,
+ sg_map->dmamap);
+ ahd_dmamem_free(ahd, scb_data->sg_dmat,
+ sg_map->vaddr, sg_map->dmamap);
+ free(sg_map, M_DEVBUF);
+ }
+ ahd_dma_tag_destroy(ahd, scb_data->sg_dmat);
+ /* FALLTHROUGH */
+ }
+ case 5:
+ {
+ struct map_node *hscb_map;
+
+ while ((hscb_map = SLIST_FIRST(&scb_data->hscb_maps)) != NULL) {
+ SLIST_REMOVE_HEAD(&scb_data->hscb_maps, links);
+ ahd_dmamap_unload(ahd, scb_data->hscb_dmat,
+ hscb_map->dmamap);
+ ahd_dmamem_free(ahd, scb_data->hscb_dmat,
+ hscb_map->vaddr, hscb_map->dmamap);
+ free(hscb_map, M_DEVBUF);
+ }
+ ahd_dma_tag_destroy(ahd, scb_data->hscb_dmat);
+ /* FALLTHROUGH */
+ }
+ case 4:
+ case 3:
+ case 2:
+ case 1:
+ case 0:
+ break;
+ }
+}
+
+/*
+ * DSP filter Bypass must be enabled until the first selection
+ * after a change in bus mode (Razor #491 and #493).
+ */
+static void
+ahd_setup_iocell_workaround(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ ahd_outb(ahd, DSPDATACTL, ahd_inb(ahd, DSPDATACTL)
+ | BYPASSENAB | RCVROFFSTDIS | XMITOFFSTDIS);
+ ahd_outb(ahd, SIMODE0, ahd_inb(ahd, SIMODE0) | (ENSELDO|ENSELDI));
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+static void
+ahd_iocell_first_selection(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ u_int sblkctl;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ sblkctl = ahd_inb(ahd, SBLKCTL);
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ if ((sblkctl & ENAB40) != 0) {
+ ahd_outb(ahd, DSPDATACTL,
+ ahd_inb(ahd, DSPDATACTL) & ~BYPASSENAB);
+ }
+ ahd_outb(ahd, SIMODE0, ahd_inb(ahd, SIMODE0) & ~(ENSELDO|ENSELDI));
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+void
+ahd_alloc_scbs(struct ahd_softc *ahd)
+{
+ struct scb_data *scb_data;
+ struct scb *next_scb;
+ struct hardware_scb *hscb;
+ struct map_node *hscb_map;
+ struct map_node *sg_map;
+ struct map_node *sense_map;
+ uint8_t *segs;
+ uint8_t *sense_data;
+ bus_addr_t hscb_busaddr;
+ bus_addr_t sg_busaddr;
+ bus_addr_t sense_busaddr;
+ int newcount;
+ int i;
+
+ scb_data = &ahd->scb_data;
+ if (scb_data->numscbs >= AHD_SCB_MAX_ALLOC)
+ /* Can't allocate any more */
+ return;
+
+ if (scb_data->scbs_left != 0) {
+ int offset;
+
+ offset = (PAGE_SIZE / sizeof(*hscb)) - scb_data->scbs_left;
+ hscb_map = SLIST_FIRST(&scb_data->hscb_maps);
+ hscb = &((struct hardware_scb *)hscb_map->vaddr)[offset];
+ hscb_busaddr = hscb_map->physaddr + (offset * sizeof(*hscb));
+ } else {
+ hscb_map = malloc(sizeof(*hscb_map), M_DEVBUF, M_NOWAIT);
+
+ if (hscb_map == NULL)
+ return;
+
+ /* Allocate the next batch of hardware SCBs */
+ if (ahd_dmamem_alloc(ahd, scb_data->hscb_dmat,
+ (void **)&hscb_map->vaddr,
+ BUS_DMA_NOWAIT, &hscb_map->dmamap) != 0) {
+ free(hscb_map, M_DEVBUF);
+ return;
+ }
+
+ SLIST_INSERT_HEAD(&scb_data->hscb_maps, hscb_map, links);
+
+ ahd_dmamap_load(ahd, scb_data->hscb_dmat, hscb_map->dmamap,
+ hscb_map->vaddr, PAGE_SIZE, ahd_dmamap_cb,
+ &hscb_map->physaddr, /*flags*/0);
+
+ hscb = (struct hardware_scb *)hscb_map->vaddr;
+ hscb_busaddr = hscb_map->physaddr;
+ scb_data->scbs_left = PAGE_SIZE / sizeof(*hscb);
+ }
+
+ if (scb_data->sgs_left != 0) {
+ int offset;
+
+ offset = ahd_sglist_allocsize(ahd)
+ - (scb_data->sgs_left * ahd_sglist_size(ahd));
+ sg_map = SLIST_FIRST(&scb_data->sg_maps);
+ segs = sg_map->vaddr + offset;
+ sg_busaddr = sg_map->physaddr + offset;
+ } else {
+ sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
+
+ if (sg_map == NULL)
+ return;
+
+ /* Allocate the next batch of S/G lists */
+ if (ahd_dmamem_alloc(ahd, scb_data->sg_dmat,
+ (void **)&sg_map->vaddr,
+ BUS_DMA_NOWAIT, &sg_map->dmamap) != 0) {
+ free(sg_map, M_DEVBUF);
+ return;
+ }
+
+ SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+ ahd_dmamap_load(ahd, scb_data->sg_dmat, sg_map->dmamap,
+ sg_map->vaddr, ahd_sglist_allocsize(ahd),
+ ahd_dmamap_cb, &sg_map->physaddr, /*flags*/0);
+
+ segs = sg_map->vaddr;
+ sg_busaddr = sg_map->physaddr;
+ scb_data->sgs_left =
+ ahd_sglist_allocsize(ahd) / ahd_sglist_size(ahd);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_MEMORY)
+ printf("Mapped SG data\n");
+#endif
+ }
+
+ if (scb_data->sense_left != 0) {
+ int offset;
+
+ offset = PAGE_SIZE - (AHD_SENSE_BUFSIZE * scb_data->sense_left);
+ sense_map = SLIST_FIRST(&scb_data->sense_maps);
+ sense_data = sense_map->vaddr + offset;
+ sense_busaddr = sense_map->physaddr + offset;
+ } else {
+ sense_map = malloc(sizeof(*sense_map), M_DEVBUF, M_NOWAIT);
+
+ if (sense_map == NULL)
+ return;
+
+ /* Allocate the next batch of sense buffers */
+ if (ahd_dmamem_alloc(ahd, scb_data->sense_dmat,
+ (void **)&sense_map->vaddr,
+ BUS_DMA_NOWAIT, &sense_map->dmamap) != 0) {
+ free(sense_map, M_DEVBUF);
+ return;
+ }
+
+ SLIST_INSERT_HEAD(&scb_data->sense_maps, sense_map, links);
+
+ ahd_dmamap_load(ahd, scb_data->sense_dmat, sense_map->dmamap,
+ sense_map->vaddr, PAGE_SIZE, ahd_dmamap_cb,
+ &sense_map->physaddr, /*flags*/0);
+
+ sense_data = sense_map->vaddr;
+ sense_busaddr = sense_map->physaddr;
+ scb_data->sense_left = PAGE_SIZE / AHD_SENSE_BUFSIZE;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_MEMORY)
+ printf("Mapped sense data\n");
+#endif
+ }
+
+ newcount = MIN(scb_data->sense_left, scb_data->scbs_left);
+ newcount = MIN(newcount, scb_data->sgs_left);
+ newcount = MIN(newcount, (AHD_SCB_MAX_ALLOC - scb_data->numscbs));
+ scb_data->sense_left -= newcount;
+ scb_data->scbs_left -= newcount;
+ scb_data->sgs_left -= newcount;
+ for (i = 0; i < newcount; i++) {
+ struct scb_platform_data *pdata;
+#ifndef __linux__
+ int error;
+#endif
+ next_scb = (struct scb *)malloc(sizeof(*next_scb),
+ M_DEVBUF, M_NOWAIT);
+ if (next_scb == NULL)
+ break;
+
+ pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
+ M_DEVBUF, M_NOWAIT);
+ if (pdata == NULL) {
+ free(next_scb, M_DEVBUF);
+ break;
+ }
+ next_scb->platform_data = pdata;
+ next_scb->hscb_map = hscb_map;
+ next_scb->sg_map = sg_map;
+ next_scb->sense_map = sense_map;
+ next_scb->sg_list = segs;
+ next_scb->sense_data = sense_data;
+ next_scb->sense_busaddr = sense_busaddr;
+ next_scb->hscb = hscb;
+ hscb->hscb_busaddr = ahd_htole32(hscb_busaddr);
+
+ /*
+ * The sequencer always starts with the second entry.
+ * The first entry is embedded in the scb.
+ */
+ next_scb->sg_list_busaddr = sg_busaddr;
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+ next_scb->sg_list_busaddr
+ += sizeof(struct ahd_dma64_seg);
+ else
+ next_scb->sg_list_busaddr += sizeof(struct ahd_dma_seg);
+ next_scb->ahd_softc = ahd;
+ next_scb->flags = SCB_FREE;
+#ifndef __linux__
+ error = ahd_dmamap_create(ahd, ahd->buffer_dmat, /*flags*/0,
+ &next_scb->dmamap);
+ if (error != 0) {
+ free(next_scb, M_DEVBUF);
+ free(pdata, M_DEVBUF);
+ break;
+ }
+#endif
+ next_scb->hscb->tag = ahd_htole16(scb_data->numscbs);
+
+ SLIST_INSERT_HEAD(&scb_data->free_scbs,
+ next_scb, links.sle);
+ hscb++;
+ hscb_busaddr += sizeof(*hscb);
+ segs += ahd_sglist_size(ahd);
+ sg_busaddr += ahd_sglist_size(ahd);
+ sense_data += AHD_SENSE_BUFSIZE;
+ sense_busaddr += AHD_SENSE_BUFSIZE;
+ scb_data->numscbs++;
+ }
+}
+
+void
+ahd_controller_info(struct ahd_softc *ahd, char *buf)
+{
+ const char *speed;
+ const char *type;
+ int len;
+
+ len = sprintf(buf, "%s: ", ahd_chip_names[ahd->chip & AHD_CHIPID_MASK]);
+ buf += len;
+
+ speed = "Ultra320 ";
+ if ((ahd->features & AHD_WIDE) != 0) {
+ type = "Wide";
+ } else {
+ type = "Single";
+ }
+ len = sprintf(buf, "%s%sChannel %c, SCSI Id=%d, ",
+ speed, type, ahd->channel, ahd->our_id);
+ buf += len;
+
+ sprintf(buf, "%s, %d SCBs", ahd->bus_description,
+ ahd->scb_data.maxhscbs);
+}
+
+static const char *channel_strings[] = {
+ "Primary Low",
+ "Primary High",
+ "Secondary Low",
+ "Secondary High"
+};
+
+static const char *termstat_strings[] = {
+ "Terminated Correctly",
+ "Over Terminated",
+ "Under Terminated",
+ "Not Configured"
+};
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahd_init(struct ahd_softc *ahd)
+{
+ size_t driver_data_size;
+ int i;
+ int error;
+ int wait;
+ u_int warn_user;
+ uint8_t current_sensing;
+ uint8_t fstat;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+ /*
+ * Verify that the compiler hasn't over-agressively
+ * padded important structures.
+ */
+ if (sizeof(struct hardware_scb) != 64)
+ panic("Hardware SCB size is incorrect");
+
+#ifdef AHD_DEBUG_SEQUENCER
+ ahd->flags |= AHD_SEQUENCER_DEBUG;
+#endif
+
+ /*
+ * Default to allowing initiator operations.
+ */
+ ahd->flags |= AHD_INITIATORROLE;
+
+ /*
+ * Only allow target mode features if this unit has them enabled.
+ */
+ if ((AHD_TMODE_ENABLE & (0x1 << ahd->unit)) == 0)
+ ahd->features &= ~AHD_TARGETMODE;
+
+#ifndef __linux__
+ /* DMA tag for mapping buffers into device visible space. */
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/MAXBSIZE, /*nsegments*/AHD_NSEG,
+ /*maxsegsz*/AHD_MAXTRANSFER_SIZE,
+ /*flags*/BUS_DMA_ALLOCNOW,
+ &ahd->buffer_dmat) != 0) {
+ return (ENOMEM);
+ }
+#endif
+
+ ahd->init_level++;
+
+ /*
+ * DMA tag for our command fifos and other data in system memory
+ * the card's sequencer must be able to access. For initiator
+ * roles, we need to allocate space for the qoutfifo. When providing
+ * for the target mode role, we must additionally provide space for
+ * the incoming target command fifo.
+ */
+ driver_data_size = AHD_SCB_MAX * sizeof(uint16_t);
+ if ((ahd->features & AHD_TARGETMODE) != 0)
+ driver_data_size += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0)
+ driver_data_size += PKT_OVERRUN_BUFSIZE;
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ driver_data_size,
+ /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &ahd->shared_data_dmat) != 0) {
+ return (ENOMEM);
+ }
+
+ ahd->init_level++;
+
+ /* Allocation of driver data */
+ if (ahd_dmamem_alloc(ahd, ahd->shared_data_dmat,
+ (void **)&ahd->qoutfifo,
+ BUS_DMA_NOWAIT, &ahd->shared_data_dmamap) != 0) {
+ return (ENOMEM);
+ }
+
+ ahd->init_level++;
+
+ /* And permanently map it in */
+ ahd_dmamap_load(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap,
+ ahd->qoutfifo, driver_data_size, ahd_dmamap_cb,
+ &ahd->shared_data_busaddr, /*flags*/0);
+
+ if ((ahd->features & AHD_TARGETMODE) != 0) {
+ /* XXX sequencer assumes qoutfifo is first. */
+ ahd->targetcmds = (struct target_cmd *)ahd->qoutfifo;
+ ahd->qoutfifo = (uint16_t *)&ahd->targetcmds[AHD_TMODE_CMDS];
+ }
+
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0)
+ ahd->overrun_buf = (uint8_t *)&ahd->qoutfifo[AHD_SCB_MAX];
+
+ ahd->init_level++;
+
+ /* Allocate SCB data now that buffer_dmat is initialized */
+ if (ahd_init_scbdata(ahd) != 0)
+ return (ENOMEM);
+
+ if ((ahd->flags & AHD_INITIATORROLE) == 0)
+ ahd->flags &= ~AHD_RESET_BUS_A;
+
+ ahd_chip_init(ahd);
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+ /*
+ * Wait for up to 500ms for our transceivers
+ * to settle. If the adapter does not have
+ * a cable attached, the tranceivers may
+ * never settle, so don't complain if we
+ * fail here.
+ */
+ for (wait = 10000;
+ (ahd_inb(ahd, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+ wait--)
+ ahd_delay(100);
+ if ((ahd->flags & AHD_CURRENT_SENSING) == 0)
+ goto init_done;
+
+ /*
+ * Verify termination based on current draw and
+ * warn user if the bus is over/under terminated.
+ */
+ error = ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL,
+ CURSENSE_ENB);
+ if (error != 0) {
+ printf("%s: current sensing timeout 1\n", ahd_name(ahd));
+ goto init_done;
+ }
+ for (i = 20, fstat = FLX_FSTAT_BUSY;
+ (fstat & FLX_FSTAT_BUSY) != 0 && i; i--) {
+ error = ahd_read_flexport(ahd, FLXADDR_FLEXSTAT, &fstat);
+ if (error != 0) {
+ printf("%s: current sensing timeout 2\n",
+ ahd_name(ahd));
+ goto init_done;
+ }
+ }
+ if (i == 0) {
+ printf("%s: Timedout during current-sensing test\n",
+ ahd_name(ahd));
+ goto init_done;
+ }
+
+ /* Latch Current Sensing status. */
+ error = ahd_read_flexport(ahd, FLXADDR_CURRENT_STAT, &current_sensing);
+ if (error != 0) {
+ printf("%s: current sensing timeout 3\n", ahd_name(ahd));
+ goto init_done;
+ }
+
+ /* Diable current sensing. */
+ ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, 0);
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_TERMCTL) != 0) {
+ printf("%s: current_sensing == 0x%x\n",
+ ahd_name(ahd), current_sensing);
+ }
+#endif
+ warn_user = 0;
+ for (i = 0; i < 4; i++, current_sensing >>= FLX_CSTAT_SHIFT) {
+ u_int term_stat;
+
+ term_stat = (current_sensing & FLX_CSTAT_MASK);
+ switch (term_stat) {
+ case FLX_CSTAT_OVER:
+ case FLX_CSTAT_UNDER:
+ warn_user++;
+ case FLX_CSTAT_INVALID:
+ case FLX_CSTAT_OKAY:
+ if (warn_user == 0 && bootverbose == 0)
+ break;
+ printf("%s: %s Channel %s\n", ahd_name(ahd),
+ channel_strings[i], termstat_strings[term_stat]);
+ break;
+ }
+ }
+ if (warn_user) {
+ printf("%s: WARNING. Termination is not configured correctly.\n"
+ "%s: WARNING. SCSI bus operations may FAIL.\n",
+ ahd_name(ahd), ahd_name(ahd));
+ }
+init_done:
+ ahd_restart(ahd);
+ return (0);
+}
+
+/*
+ * (Re)initialize chip state after a chip reset.
+ */
+static void
+ahd_chip_init(struct ahd_softc *ahd)
+{
+ uint32_t busaddr;
+ u_int sxfrctl1;
+ u_int scsiseq_template;
+ u_int i;
+ u_int target;
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ ahd_outb(ahd, SBLKCTL, ahd_inb(ahd, SBLKCTL) & ~(DIAGLEDEN|DIAGLEDON));
+
+ /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1. */
+ ahd_outb(ahd, IOWNID, ahd->our_id);
+ ahd_outb(ahd, TOWNID, ahd->our_id);
+ sxfrctl1 = (ahd->flags & AHD_TERM_ENB_A) != 0 ? STPWEN : 0;
+ sxfrctl1 |= (ahd->flags & AHD_SPCHK_ENB_A) != 0 ? ENSPCHK : 0;
+ if ((ahd->bugs & AHD_LONG_SETIMO_BUG)
+ && (ahd->seltime != STIMESEL_MIN)) {
+ /*
+ * The selection timer duration is twice as long
+ * as it should be. Halve it by adding "1" to
+ * the user specified setting.
+ */
+ sxfrctl1 |= ahd->seltime + STIMESEL_BUG_ADJ;
+ } else {
+ sxfrctl1 |= ahd->seltime;
+ }
+
+ ahd_outb(ahd, SXFRCTL0, DFON);
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1|ahd->seltime|ENSTIMER|ACTNEGEN);
+ ahd_outb(ahd, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+
+ /* Initialize mode specific S/G state. */
+ for (i = 0; i < 2; i++) {
+ ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
+ ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+ ahd_outw(ahd, LONGJMP_SCB, SCB_LIST_NULL);
+ ahd_outb(ahd, SG_STATE, 0);
+ ahd_outb(ahd, CLRSEQINTSRC, 0xFF);
+ ahd_outb(ahd, SEQIMODE,
+ ENSAVEPTRS|ENCFG4DATA|ENCFG4ISTAT
+ |ENCFG4TSTAT|ENCFG4ICMD|ENCFG4TCMD);
+ }
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ ahd_outb(ahd, DSCOMMAND0, ahd_inb(ahd, DSCOMMAND0)|MPARCKEN|CACHETHEN);
+ ahd_outb(ahd, DFF_THRSH, RD_DFTHRSH_75|WR_DFTHRSH_75);
+ ahd_outb(ahd, SIMODE0, ENIOERR|ENOVERRUN);
+ ahd_outb(ahd, SIMODE3, ENNTRAMPERR|ENOSRAMPERR);
+ ahd_outb(ahd, OPTIONMODE, AUTOACKEN|BUSFREEREV|AUTO_MSGOUT_DE);
+ if ((ahd->chip & AHD_BUS_MASK) == AHD_PCIX)
+ /*
+ * Do not issue a target abort when a split completion
+ * error occurs. Let our PCIX interrupt handler deal
+ * with it instead. H2A4 Razor #625
+ */
+ ahd_outb(ahd, PCIXCTL, ahd_inb(ahd, PCIXCTL) | SPLTSTADIS);
+
+ /*
+ * Tweak IOCELL settings.
+ */
+ if ((ahd->flags & AHD_CPQ_BOARD) != 0) {
+ for (i = 0; i < NUMDSPS; i++) {
+ ahd_outb(ahd, DSPSELECT, i);
+ ahd_outb(ahd, WRTBIASCTL, WRTBIASCTL_CPQ_DEFAULT);
+ }
+ }
+ ahd_setup_iocell_workaround(ahd);
+
+ /*
+ * Enable LQI Manager interrupts.
+ */
+ ahd_outb(ahd, LQIMODE1, ENLQIPHASE_LQ|ENLQIPHASE_NLQ|ENLIQABORT
+ | ENLQICRCI_LQ|ENLQICRCI_NLQ|ENLQIBADLQI
+ | ENLQIOVERI_LQ|ENLQIOVERI_NLQ);
+ ahd_outb(ahd, LQOMODE0, ENLQOATNLQ|ENLQOATNPKT|ENLQOTCRC);
+ /*
+ * An interrupt from LQOBUSFREE is made redundant by the
+ * BUSFREE interrupt. We choose to have the sequencer catch
+ * LQOPHCHGINPKT errors manually for the command phase at the
+ * start of a packetized selection case.
+ ahd_outb(ahd, LQOMODE1, ENLQOBUSFREE|ENLQOPHACHGINPKT);
+ */
+ ahd_outb(ahd, LQOMODE1, 0);
+
+ /*
+ * Setup sequencer interrupt handler.
+ */
+ ahd_outw(ahd, INTVEC1_ADDR, ahd_resolve_seqaddr(ahd, LABEL_seq_isr));
+
+ /*
+ * Setup SCB Offset registers.
+ */
+ ahd_outb(ahd, LUNPTR, offsetof(struct hardware_scb, lun));
+ ahd_outb(ahd, CMDLENPTR, offsetof(struct hardware_scb, cdb_len));
+ ahd_outb(ahd, ATTRPTR,
+ offsetof(struct hardware_scb, task_attribute_nonpkt_tag));
+ ahd_outb(ahd, FLAGPTR, offsetof(struct hardware_scb, task_management));
+ ahd_outb(ahd, CMDPTR, offsetof(struct hardware_scb,
+ shared_data.idata.cdb));
+ ahd_outb(ahd, QNEXTPTR,
+ offsetof(struct hardware_scb, next_hscb_busaddr));
+ ahd_outb(ahd, ABRTBITPTR, MK_MESSAGE_BIT_OFFSET);
+ ahd_outb(ahd, ABRTBYTEPTR, offsetof(struct hardware_scb, control));
+ ahd_outb(ahd, LUNLEN, sizeof(ahd->next_queued_scb->hscb->lun) - 1);
+ ahd_outb(ahd, CDBLIMIT, SCB_CDB_LEN_PTR - 1);
+ ahd_outb(ahd, MAXCMD, 0xFF);
+ ahd_outb(ahd, SCBAUTOPTR,
+ AUSCBPTR_EN | offsetof(struct hardware_scb, tag));
+
+ /* We haven't been enabled for target mode yet. */
+ ahd_outb(ahd, MULTARGID, 0);
+ ahd_outb(ahd, MULTARGID + 1, 0);
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ /*
+ * Clear the spare bytes in the neg table to avoid
+ * spurious parity errors.
+ */
+ for (target = 0; target < AHD_NUM_TARGETS; target++) {
+
+ ahd_outb(ahd, NEGOADDR, target);
+ ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_PRECOMP);
+ for (i = 0; i < AHD_NUM_ANNEXCOLS; i++)
+ ahd_outb(ahd, ANNEXDAT, 0);
+ }
+ ahd_outb(ahd, CLRSINT3, NTRAMPERR|OSRAMPERR);
+
+ /*
+ * Always enable abort on incoming L_Qs if this feature is
+ * supported. We use this to catch invalid SCB references.
+ */
+ if ((ahd->bugs & AHD_ABORT_LQI_BUG) == 0)
+ ahd_outb(ahd, LQCTL1, ABORTPENDING);
+ else
+ ahd_outb(ahd, LQCTL1, 0);
+
+ /* All of our queues are empty */
+ ahd->qoutfifonext = 0;
+ for (i = 0; i < AHD_QOUT_SIZE; i++)
+ ahd->qoutfifo[i] = SCB_LIST_NULL_LE;
+ ahd_sync_qoutfifo(ahd, BUS_DMASYNC_PREREAD);
+
+ ahd->qinfifonext = 0;
+ for (i = 0; i < AHD_QIN_SIZE; i++)
+ ahd->qinfifo[i] = SCB_LIST_NULL;
+
+ if ((ahd->features & AHD_TARGETMODE) != 0) {
+ /* All target command blocks start out invalid. */
+ for (i = 0; i < AHD_TMODE_CMDS; i++)
+ ahd->targetcmds[i].cmd_valid = 0;
+ ahd_sync_tqinfifo(ahd, BUS_DMASYNC_PREREAD);
+ ahd->tqinfifonext = 1;
+ ahd_outb(ahd, KERNEL_TQINPOS, ahd->tqinfifonext - 1);
+ ahd_outb(ahd, TQINPOS, ahd->tqinfifonext);
+ }
+
+ /* Initialize Scratch Ram. */
+ ahd_outb(ahd, SEQ_FLAGS, 0);
+ ahd_outb(ahd, SEQ_FLAGS2, 0);
+
+ /* We don't have any waiting selections */
+ ahd_outw(ahd, WAITING_TID_HEAD, SCB_LIST_NULL);
+ ahd_outw(ahd, WAITING_TID_TAIL, SCB_LIST_NULL);
+ for (i = 0; i < AHD_NUM_TARGETS; i++)
+ ahd_outw(ahd, WAITING_SCB_TAILS + (2 * i), SCB_LIST_NULL);
+
+ /*
+ * Nobody is waiting to be DMAed into the QOUTFIFO.
+ */
+ ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);
+ ahd_outw(ahd, COMPLETE_SCB_DMAINPROG_HEAD, SCB_LIST_NULL);
+ ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);
+
+ /*
+ * The Freeze Count is 0.
+ */
+ ahd_outw(ahd, QFREEZE_COUNT, 0);
+
+ /*
+ * Tell the sequencer where it can find our arrays in memory.
+ */
+ busaddr = ahd->shared_data_busaddr;
+ ahd_outb(ahd, SHARED_DATA_ADDR, busaddr & 0xFF);
+ ahd_outb(ahd, SHARED_DATA_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, SHARED_DATA_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, SHARED_DATA_ADDR + 3, (busaddr >> 24) & 0xFF);
+ ahd_outb(ahd, QOUTFIFO_NEXT_ADDR, busaddr & 0xFF);
+ ahd_outb(ahd, QOUTFIFO_NEXT_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, QOUTFIFO_NEXT_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, QOUTFIFO_NEXT_ADDR + 3, (busaddr >> 24) & 0xFF);
+
+ /*
+ * Setup the allowed SCSI Sequences based on operational mode.
+ * If we are a target, we'll enable select in operations once
+ * we've had a lun enabled.
+ */
+ scsiseq_template = ENAUTOATNP;
+ if ((ahd->flags & AHD_INITIATORROLE) != 0)
+ scsiseq_template |= ENRSELI;
+ ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq_template);
+
+ /* There are no busy SCBs yet. */
+ for (target = 0; target < AHD_NUM_TARGETS; target++) {
+ int lun;
+
+ for (lun = 0; lun < AHD_NUM_LUNS_NONPKT; lun++)
+ ahd_unbusy_tcl(ahd, BUILD_TCL_RAW(target, 'A', lun));
+ }
+
+ /*
+ * Always enable abort on incoming L_Qs if this feature is
+ * supported. We use this to catch invalid SCB references.
+ */
+ if ((ahd->bugs & AHD_ABORT_LQI_BUG) == 0)
+ ahd_outb(ahd, LQCTL1, ABORTPENDING);
+ else
+ ahd_outb(ahd, LQCTL1, 0);
+
+ /*
+ * Initialize the group code to command length table.
+ * Vendor Unique codes are set to 0 so we only capture
+ * the first byte of the cdb. These can be overridden
+ * when target mode is enabled.
+ */
+ ahd_outb(ahd, CMDSIZE_TABLE, 5);
+ ahd_outb(ahd, CMDSIZE_TABLE + 1, 9);
+ ahd_outb(ahd, CMDSIZE_TABLE + 2, 9);
+ ahd_outb(ahd, CMDSIZE_TABLE + 3, 0);
+ ahd_outb(ahd, CMDSIZE_TABLE + 4, 15);
+ ahd_outb(ahd, CMDSIZE_TABLE + 5, 11);
+ ahd_outb(ahd, CMDSIZE_TABLE + 6, 0);
+ ahd_outb(ahd, CMDSIZE_TABLE + 7, 0);
+
+ /* Tell the sequencer of our initial queue positions */
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+ ahd_outb(ahd, QOFF_CTLSTA, SCB_QSIZE_512);
+ ahd->qinfifonext = 0;
+ ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+ ahd_set_hescb_qoff(ahd, 0);
+ ahd_set_snscb_qoff(ahd, 0);
+ ahd_set_sescb_qoff(ahd, 0);
+ ahd_set_sdscb_qoff(ahd, 0);
+
+ /*
+ * Tell the sequencer which SCB will be the next one it receives.
+ */
+ busaddr = ahd_le32toh(ahd->next_queued_scb->hscb->hscb_busaddr);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 0, busaddr & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF);
+ ahd_loadseq(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+}
+
+/*
+ * Setup default device and controller settings.
+ * This should only be called if our probe has
+ * determined that no configuration data is available.
+ */
+int
+ahd_default_config(struct ahd_softc *ahd)
+{
+ int targ;
+
+ ahd->our_id = 7;
+
+ /*
+ * Allocate a tstate to house information for our
+ * initiator presence on the bus as well as the user
+ * data for any target mode initiator.
+ */
+ if (ahd_alloc_tstate(ahd, ahd->our_id, 'A') == NULL) {
+ printf("%s: unable to allocate ahd_tmode_tstate. "
+ "Failing attach\n", ahd_name(ahd));
+ return (ENOMEM);
+ }
+
+ for (targ = 0; targ < AHD_NUM_TARGETS; targ++) {
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ uint16_t target_mask;
+
+ TAILQ_INIT(&ahd->untagged_queues[targ]);
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ targ, &tstate);
+ /*
+ * We support SPC2 and SPI4.
+ */
+ tinfo->user.protocol_version = 4;
+ tinfo->user.transport_version = 4;
+
+ target_mask = 0x01 << targ;
+ ahd->user_discenable |= target_mask;
+ tstate->discenable |= target_mask;
+ ahd->user_tagenable |= target_mask;
+#ifdef AHD_FORCE_160
+ tinfo->user.period = AHD_SYNCRATE_DT;
+#else
+ tinfo->user.period = AHD_SYNCRATE_160;
+#endif
+ tinfo->user.offset= ~0;
+ tinfo->user.ppr_options = MSG_EXT_PPR_RD_STRM
+ | MSG_EXT_PPR_WR_FLOW
+ | MSG_EXT_PPR_IU_REQ
+ | MSG_EXT_PPR_QAS_REQ
+ | MSG_EXT_PPR_DT_REQ;
+
+ tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+ /*
+ * Start out Async/Narrow/Untagged and with
+ * conservative protocol support.
+ */
+ tinfo->goal.protocol_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->curr.protocol_version = 2;
+ tinfo->curr.transport_version = 2;
+ ahd_compile_devinfo(&devinfo, ahd->our_id,
+ targ, CAM_LUN_WILDCARD,
+ 'A', ROLE_INITIATOR);
+ tstate->tagenable &= ~target_mask;
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE);
+ ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0,
+ /*ppr_options*/0, AHD_TRANS_CUR|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ /*
+ * The neg table must be initialized even if the
+ * new settings above are the same as those from
+ * when our xfer info data structures were allocated
+ * and initialized.
+ */
+ ahd_update_neg_table(ahd, &devinfo, &tinfo->curr);
+ }
+ return (0);
+}
+
+/*
+ * Parse device configuration information.
+ */
+int
+ahd_parse_cfgdata(struct ahd_softc *ahd, struct seeprom_config *sc)
+{
+ int targ;
+ int max_targ;
+
+ max_targ = sc->max_targets & CFMAXTARG;
+ ahd->our_id = sc->brtime_id & CFSCSIID;
+
+ /*
+ * Allocate a tstate to house information for our
+ * initiator presence on the bus as well as the user
+ * data for any target mode initiator.
+ */
+ if (ahd_alloc_tstate(ahd, ahd->our_id, 'A') == NULL) {
+ printf("%s: unable to allocate ahd_tmode_tstate. "
+ "Failing attach\n", ahd_name(ahd));
+ return (ENOMEM);
+ }
+
+ for (targ = 0; targ < max_targ; targ++) {
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_transinfo *user_tinfo;
+ struct ahd_tmode_tstate *tstate;
+ uint16_t target_mask;
+
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ targ, &tstate);
+ user_tinfo = &tinfo->user;
+
+ /*
+ * We support SPC2 and SPI4.
+ */
+ tinfo->user.protocol_version = 4;
+ tinfo->user.transport_version = 4;
+
+ target_mask = 0x01 << targ;
+ ahd->user_discenable &= ~target_mask;
+ tstate->discenable &= ~target_mask;
+ ahd->user_tagenable &= ~target_mask;
+ if (sc->device_flags[targ] & CFDISC) {
+ tstate->discenable |= target_mask;
+ ahd->user_discenable |= target_mask;
+ ahd->user_tagenable |= target_mask;
+ } else {
+ /*
+ * Cannot be packetized without disconnection.
+ */
+ sc->device_flags[targ] &= ~CFPACKETIZED;
+ }
+
+ user_tinfo->ppr_options = 0;
+ user_tinfo->period = (sc->device_flags[targ] & CFXFER);
+ if (user_tinfo->period < CFXFER_ASYNC) {
+ if (user_tinfo->period <= AHD_PERIOD_10MHz)
+ user_tinfo->ppr_options |= MSG_EXT_PPR_DT_REQ;
+ user_tinfo->offset = MAX_OFFSET;
+ } else {
+ user_tinfo->offset = 0;
+ user_tinfo->period = AHD_PERIOD_ASYNC;
+ }
+#ifdef AHD_FORCE_160
+ if (user_tinfo->period <= AHD_SYNCRATE_160)
+ user_tinfo->period = AHD_SYNCRATE_DT;
+#endif
+
+ if ((sc->device_flags[targ] & CFPACKETIZED) != 0)
+ user_tinfo->ppr_options |= MSG_EXT_PPR_RD_STRM
+ | MSG_EXT_PPR_WR_FLOW
+ | MSG_EXT_PPR_IU_REQ;
+
+ if ((sc->device_flags[targ] & CFQAS) != 0)
+ user_tinfo->ppr_options |= MSG_EXT_PPR_QAS_REQ;
+
+ if ((sc->device_flags[targ] & CFWIDEB) != 0)
+ user_tinfo->width = MSG_EXT_WDTR_BUS_16_BIT;
+ else
+ user_tinfo->width = MSG_EXT_WDTR_BUS_8_BIT;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("(%d): %x:%x:%x:%x\n", targ, user_tinfo->width,
+ user_tinfo->period, user_tinfo->offset,
+ user_tinfo->ppr_options);
+#endif
+ /*
+ * Start out Async/Narrow/Untagged and with
+ * conservative protocol support.
+ */
+ tstate->tagenable &= ~target_mask;
+ tinfo->goal.protocol_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->curr.protocol_version = 2;
+ tinfo->curr.transport_version = 2;
+ ahd_compile_devinfo(&devinfo, ahd->our_id,
+ targ, CAM_LUN_WILDCARD,
+ 'A', ROLE_INITIATOR);
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE);
+ ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0,
+ /*ppr_options*/0, AHD_TRANS_CUR|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ /*
+ * The neg table must be initialized even if the
+ * new settings above are the same as those from
+ * when our xfer info data structures were allocated
+ * and initialized.
+ */
+ ahd_update_neg_table(ahd, &devinfo, &tinfo->curr);
+ }
+
+ ahd->flags &= ~AHD_SPCHK_ENB_A;
+ if (sc->bios_control & CFSPARITY)
+ ahd->flags |= AHD_SPCHK_ENB_A;
+
+ ahd->flags &= ~AHD_RESET_BUS_A;
+ if (sc->bios_control & CFRESETB)
+ ahd->flags |= AHD_RESET_BUS_A;
+
+ ahd->flags &= ~AHD_EXTENDED_TRANS_A;
+ if (sc->bios_control & CFEXTEND)
+ ahd->flags |= AHD_EXTENDED_TRANS_A;
+
+ ahd->flags &= ~AHD_BIOS_ENABLED;
+ if ((sc->bios_control & CFBIOSSTATE) == CFBS_ENABLED)
+ ahd->flags |= AHD_BIOS_ENABLED;
+
+ ahd->flags &= ~AHD_STPWLEVEL_A;
+ if ((sc->adapter_control & CFSTPWLEVEL) != 0)
+ ahd->flags |= AHD_STPWLEVEL_A;
+
+ return (0);
+}
+
+void
+ahd_intr_enable(struct ahd_softc *ahd, int enable)
+{
+ u_int hcntrl;
+
+ hcntrl = ahd_inb(ahd, HCNTRL);
+ hcntrl &= ~INTEN;
+ ahd->pause &= ~INTEN;
+ ahd->unpause &= ~INTEN;
+ if (enable) {
+ hcntrl |= INTEN;
+ ahd->pause |= INTEN;
+ ahd->unpause |= INTEN;
+ }
+ ahd_outb(ahd, HCNTRL, hcntrl);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahd_pause_and_flushwork(struct ahd_softc *ahd)
+{
+ int intstat;
+ int maxloops;
+
+ maxloops = 1000;
+ ahd->flags |= AHD_ALL_INTERRUPTS;
+ intstat = 0;
+ do {
+ ahd_intr(ahd);
+ ahd_pause(ahd);
+ ahd_clear_critical_section(ahd);
+ if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0)
+ break;
+ maxloops--;
+ } while (((intstat = ahd_inb(ahd, INTSTAT)) & INT_PEND) && --maxloops);
+ if (maxloops == 0) {
+ printf("Infinite interrupt loop, INTSTAT = %x",
+ ahd_inb(ahd, INTSTAT));
+ }
+ ahd_platform_flushwork(ahd);
+ ahd->flags &= ~AHD_ALL_INTERRUPTS;
+}
+
+int
+ahd_suspend(struct ahd_softc *ahd)
+{
+#if 0
+ uint8_t *ptr;
+ int i;
+
+ ahd_pause_and_flushwork(ahd);
+
+ if (LIST_FIRST(&ahd->pending_scbs) != NULL)
+ return (EBUSY);
+
+#if AHD_TARGET_MODE
+ /*
+ * XXX What about ATIOs that have not yet been serviced?
+ * Perhaps we should just refuse to be suspended if we
+ * are acting in a target role.
+ */
+ if (ahd->pending_device != NULL)
+ return (EBUSY);
+#endif
+
+ /* Save volatile registers */
+ ahd->suspend_state.channel[0].scsiseq = ahd_inb(ahd, SCSISEQ0);
+ ahd->suspend_state.channel[0].sxfrctl0 = ahd_inb(ahd, SXFRCTL0);
+ ahd->suspend_state.channel[0].sxfrctl1 = ahd_inb(ahd, SXFRCTL1);
+ ahd->suspend_state.channel[0].simode0 = ahd_inb(ahd, SIMODE0);
+ ahd->suspend_state.channel[0].simode1 = ahd_inb(ahd, SIMODE1);
+ ahd->suspend_state.channel[0].seltimer = ahd_inb(ahd, SELTIMER);
+ ahd->suspend_state.channel[0].seqctl = ahd_inb(ahd, SEQCTL0);
+ ahd->suspend_state.dscommand0 = ahd_inb(ahd, DSCOMMAND0);
+ ahd->suspend_state.dspcistatus = ahd_inb(ahd, DSPCISTATUS);
+
+ if ((ahd->features & AHD_DT) != 0) {
+ u_int sfunct;
+
+ sfunct = ahd_inb(ahd, SFUNCT) & ~ALT_MODE;
+ ahd_outb(ahd, SFUNCT, sfunct | ALT_MODE);
+ ahd->suspend_state.optionmode = ahd_inb(ahd, OPTIONMODE);
+ ahd_outb(ahd, SFUNCT, sfunct);
+ ahd->suspend_state.crccontrol1 = ahd_inb(ahd, CRCCONTROL1);
+ }
+
+ if ((ahd->features & AHD_MULTI_FUNC) != 0)
+ ahd->suspend_state.scbbaddr = ahd_inb(ahd, SCBBADDR);
+
+ if ((ahd->features & AHD_ULTRA2) != 0)
+ ahd->suspend_state.dff_thrsh = ahd_inb(ahd, DFF_THRSH);
+
+ ptr = ahd->suspend_state.scratch_ram;
+ for (i = 0; i < 64; i++)
+ *ptr++ = ahd_inb(ahd, SRAM_BASE + i);
+
+ if ((ahd->features & AHD_MORE_SRAM) != 0) {
+ for (i = 0; i < 16; i++)
+ *ptr++ = ahd_inb(ahd, TARG_OFFSET + i);
+ }
+
+ ptr = ahd->suspend_state.btt;
+ for (i = 0;i < AHD_NUM_TARGETS; i++) {
+ int j;
+
+ for (j = 0;j < AHD_NUM_LUNS; j++) {
+ u_int tcl;
+
+ tcl = BUILD_TCL(i << 4, j);
+ *ptr = ahd_find_busy_tcl(ahd, tcl);
+ }
+ }
+ ahd_shutdown(ahd);
+#endif
+ return (0);
+}
+
+int
+ahd_resume(struct ahd_softc *ahd)
+{
+#if 0
+ uint8_t *ptr;
+ int i;
+
+ ahd_reset(ahd);
+
+ ahd_build_free_scb_list(ahd);
+
+ /* Restore volatile registers */
+ ahd_outb(ahd, SCSISEQ0, ahd->suspend_state.channel[0].scsiseq);
+ ahd_outb(ahd, SXFRCTL0, ahd->suspend_state.channel[0].sxfrctl0);
+ ahd_outb(ahd, SXFRCTL1, ahd->suspend_state.channel[0].sxfrctl1);
+ ahd_outb(ahd, SIMODE0, ahd->suspend_state.channel[0].simode0);
+ ahd_outb(ahd, SIMODE1, ahd->suspend_state.channel[0].simode1);
+ ahd_outb(ahd, SELTIMER, ahd->suspend_state.channel[0].seltimer);
+ ahd_outb(ahd, SEQCTL0, ahd->suspend_state.channel[0].seqctl);
+ if ((ahd->features & AHD_ULTRA2) != 0)
+ ahd_outb(ahd, SCSIID_ULTRA2, ahd->our_id);
+ else
+ ahd_outb(ahd, SCSIID, ahd->our_id);
+
+ ahd_outb(ahd, DSCOMMAND0, ahd->suspend_state.dscommand0);
+ ahd_outb(ahd, DSPCISTATUS, ahd->suspend_state.dspcistatus);
+
+ if ((ahd->features & AHD_DT) != 0) {
+ u_int sfunct;
+
+ sfunct = ahd_inb(ahd, SFUNCT) & ~ALT_MODE;
+ ahd_outb(ahd, SFUNCT, sfunct | ALT_MODE);
+ ahd_outb(ahd, OPTIONMODE, ahd->suspend_state.optionmode);
+ ahd_outb(ahd, SFUNCT, sfunct);
+ ahd_outb(ahd, CRCCONTROL1, ahd->suspend_state.crccontrol1);
+ }
+
+ if ((ahd->features & AHD_MULTI_FUNC) != 0)
+ ahd_outb(ahd, SCBBADDR, ahd->suspend_state.scbbaddr);
+
+ if ((ahd->features & AHD_ULTRA2) != 0)
+ ahd_outb(ahd, DFF_THRSH, ahd->suspend_state.dff_thrsh);
+
+ ptr = ahd->suspend_state.scratch_ram;
+ for (i = 0; i < 64; i++)
+ ahd_outb(ahd, SRAM_BASE + i, *ptr++);
+
+ if ((ahd->features & AHD_MORE_SRAM) != 0) {
+ for (i = 0; i < 16; i++)
+ ahd_outb(ahd, TARG_OFFSET + i, *ptr++);
+ }
+
+ ptr = ahd->suspend_state.btt;
+ for (i = 0;i < AHD_NUM_TARGETS; i++) {
+ int j;
+
+ for (j = 0;j < AHD_NUM_LUNS; j++) {
+ u_int tcl;
+
+ tcl = BUILD_TCL(i << 4, j);
+ ahd_busy_tcl(ahd, tcl, *ptr);
+ }
+ }
+#endif
+ return (0);
+}
+
+/************************** Busy Target Table *********************************/
+/*
+ * Set SCBPTR to the SCB that contains the busy
+ * table entry for TCL. Return the offset into
+ * the SCB that contains the entry for TCL.
+ * saved_scbid is dereferenced and set to the
+ * scbid that should be restored once manipualtion
+ * of the TCL entry is complete.
+ */
+static __inline u_int
+ahd_index_busy_tcl(struct ahd_softc *ahd, u_int *saved_scbid, u_int tcl)
+{
+
+ *saved_scbid = ahd_get_scbptr(ahd);
+
+ /*
+ * Index to the SCB that contains the busy entry.
+ */
+ ahd_set_scbptr(ahd, TCL_LUN(tcl)
+ | ((TCL_TARGET_OFFSET(tcl) & ~0x7) << 5));
+
+ /*
+ * And now calculate the SCB offset to the entry.
+ * Each entry is 2 bytes wide, hence the
+ * multiplication by 2.
+ */
+ return (((TCL_TARGET_OFFSET(tcl) & 0x7) << 1) + SCB_DISCONNECTED_LISTS);
+}
+
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ * Optionally, clear the entry.
+ */
+u_int
+ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl)
+{
+ u_int scbid;
+ u_int scb_offset;
+ u_int saved_scbptr;
+
+ scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
+ scbid = ahd_inw_scbram(ahd, scb_offset);
+ ahd_set_scbptr(ahd, saved_scbptr);
+ return (scbid);
+}
+
+void
+ahd_busy_tcl(struct ahd_softc *ahd, u_int tcl, u_int scbid)
+{
+ u_int scb_offset;
+ u_int saved_scbptr;
+
+ scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
+ ahd_outw(ahd, scb_offset, scbid);
+ ahd_set_scbptr(ahd, saved_scbptr);
+}
+
+void
+ahd_set_disconnected_list(struct ahd_softc *ahd, u_int target,
+ u_int lun, u_int scbid)
+{
+ u_int saved_scbptr;
+
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ saved_scbptr = ahd_get_scbptr(ahd);
+ ahd_set_scbptr(ahd, lun | ((target & 0x8) << 5));
+ ahd_outw(ahd, SCB_DISCONNECTED_LISTS + ((target & 0x7) << 1), scbid);
+ ahd_set_scbptr(ahd, saved_scbptr);
+}
+
+/************************** SCB and SCB queue management **********************/
+int
+ahd_match_scb(struct ahd_softc *ahd, struct scb *scb, int target,
+ char channel, int lun, u_int tag, role_t role)
+{
+ int targ = SCB_GET_TARGET(ahd, scb);
+ char chan = SCB_GET_CHANNEL(ahd, scb);
+ int slun = SCB_GET_LUN(scb);
+ int match;
+
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+ if (match != 0)
+ match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+ if (match != 0) {
+#if AHD_TARGET_MODE
+ int group;
+
+ group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+ if (role == ROLE_INITIATOR) {
+ match = (group != XPT_FC_GROUP_TMODE)
+ && ((tag == SCB_GET_TAG(scb))
+ || (tag == SCB_LIST_NULL));
+ } else if (role == ROLE_TARGET) {
+ match = (group == XPT_FC_GROUP_TMODE)
+ && ((tag == scb->io_ctx->csio.tag_id)
+ || (tag == SCB_LIST_NULL));
+ }
+#else /* !AHD_TARGET_MODE */
+ match = ((tag == SCB_GET_TAG(scb)) || (tag == SCB_LIST_NULL));
+#endif /* AHD_TARGET_MODE */
+ }
+
+ return match;
+}
+
+void
+ahd_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
+{
+ int target;
+ char channel;
+ int lun;
+
+ target = SCB_GET_TARGET(ahd, scb);
+ lun = SCB_GET_LUN(scb);
+ channel = SCB_GET_CHANNEL(ahd, scb);
+
+ ahd_search_qinfifo(ahd, target, channel, lun,
+ /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+ CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ ahd_platform_freeze_devq(ahd, scb);
+}
+
+void
+ahd_qinfifo_requeue_tail(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct scb *prev_scb;
+
+ prev_scb = NULL;
+ if (ahd_qinfifo_count(ahd) != 0) {
+ u_int prev_tag;
+ u_int prev_pos;
+
+ prev_pos = AHD_QIN_WRAP(ahd->qinfifonext - 1);
+ prev_tag = ahd->qinfifo[prev_pos];
+ prev_scb = ahd_lookup_scb(ahd, prev_tag);
+ }
+ ahd_qinfifo_requeue(ahd, prev_scb, scb);
+ ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+}
+
+static void
+ahd_qinfifo_requeue(struct ahd_softc *ahd, struct scb *prev_scb,
+ struct scb *scb)
+{
+ if (prev_scb == NULL) {
+ uint32_t busaddr;
+
+ busaddr = ahd_le32toh(scb->hscb->hscb_busaddr);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 0, busaddr & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF);
+ } else {
+ prev_scb->hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;
+ ahd_sync_scb(ahd, prev_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ }
+ ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
+ ahd->qinfifonext++;
+ scb->hscb->next_hscb_busaddr = ahd->next_queued_scb->hscb->hscb_busaddr;
+ ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+static int
+ahd_qinfifo_count(struct ahd_softc *ahd)
+{
+ u_int qinpos;
+ u_int wrap_qinpos;
+ u_int wrap_qinfifonext;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ qinpos = ahd_get_snscb_qoff(ahd);
+ wrap_qinpos = AHD_QIN_WRAP(qinpos);
+ wrap_qinfifonext = AHD_QIN_WRAP(ahd->qinfifonext);
+ if (wrap_qinfifonext > wrap_qinpos)
+ return (wrap_qinfifonext - wrap_qinpos);
+ else
+ return (wrap_qinfifonext
+ + NUM_ELEMENTS(ahd->qinfifo) - wrap_qinpos);
+}
+
+int
+ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status,
+ ahd_search_action action)
+{
+ struct scb *scb;
+ struct scb *prev_scb;
+ ahd_mode_state saved_modes;
+ u_int qinstart;
+ u_int qinpos;
+ u_int qintail;
+ u_int tid_next;
+ u_int tid_prev;
+ u_int scbid;
+ u_int savedscbptr;
+ uint32_t busaddr;
+ int found;
+ int targets;
+
+ /* Must be in CCHAN mode */
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+ /*
+ * Halt any pending SCB DMA. The sequencer will reinitiate
+ * this dma if the qinfifo is not empty once we unpause.
+ */
+ if ((ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN|CCSCBDIR))
+ == (CCARREN|CCSCBEN|CCSCBDIR)) {
+ ahd_outb(ahd, CCSCBCTL,
+ ahd_inb(ahd, CCSCBCTL) & ~(CCARREN|CCSCBEN));
+ while ((ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN)) != 0)
+ ;
+ }
+ /* Determine sequencer's position in the qinfifo. */
+ qintail = AHD_QIN_WRAP(ahd->qinfifonext);
+ qinstart = ahd_get_snscb_qoff(ahd);
+ qinpos = AHD_QIN_WRAP(qinstart);
+ found = 0;
+ prev_scb = NULL;
+
+ if (action == SEARCH_COMPLETE) {
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahd_freeze_untagged_queues(ahd);
+ }
+
+ if (action == SEARCH_PRINT) {
+ printf("qinstart = %d qinfifonext = %d\nQINFIFO:",
+ qinstart, ahd->qinfifonext);
+ }
+
+ /*
+ * Start with an empty queue. Entries that are not chosen
+ * for removal will be re-added to the queue as we go.
+ */
+ ahd->qinfifonext = qinstart;
+ busaddr = ahd_le32toh(ahd->next_queued_scb->hscb->hscb_busaddr);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 0, busaddr & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF);
+
+ while (qinpos != qintail) {
+ scb = ahd_lookup_scb(ahd, ahd->qinfifo[qinpos]);
+ if (scb == NULL) {
+ printf("qinpos = %d, SCB index = %d\n",
+ qinpos, ahd->qinfifo[qinpos]);
+ panic("Loop 1\n");
+ }
+
+ if (ahd_match_scb(ahd, scb, target, channel, lun, tag, role)) {
+ /*
+ * We found an scb that needs to be acted on.
+ */
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahd_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahd_set_transaction_status(scb,
+ status);
+ cstat = ahd_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahd_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in qinfifo\n");
+ ahd_done(ahd, scb);
+
+ /* FALLTHROUGH */
+ }
+ case SEARCH_REMOVE:
+ break;
+ case SEARCH_PRINT:
+ printf(" 0x%x", ahd->qinfifo[qinpos]);
+ /* FALLTHROUGH */
+ case SEARCH_COUNT:
+ ahd_qinfifo_requeue(ahd, prev_scb, scb);
+ prev_scb = scb;
+ break;
+ }
+ } else {
+ ahd_qinfifo_requeue(ahd, prev_scb, scb);
+ prev_scb = scb;
+ }
+ qinpos = AHD_QIN_WRAP(qinpos+1);
+ }
+
+ ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+
+ if (action == SEARCH_PRINT)
+ printf("\nWAITING_TID_QUEUES:\n");
+
+ /*
+ * Search waiting for selection lists. We traverse the
+ * list of "their ids" waiting for selection and, if
+ * appropriate, traverse the SCBs of each "their id"
+ * looking for matches.
+ */
+ savedscbptr = ahd_get_scbptr(ahd);
+ tid_next = ahd_inw(ahd, WAITING_TID_HEAD);
+ tid_prev = SCB_LIST_NULL;
+ targets = 0;
+ for (scbid = tid_next; !SCBID_IS_NULL(scbid); scbid = tid_next) {
+ u_int tid_head;
+
+ /*
+ * We limit based on the number of SCBs since
+ * MK_MESSAGE SCBs are not in the per-tid lists.
+ */
+ targets++;
+ if (targets > AHD_SCB_MAX) {
+ panic("TID LIST LOOP");
+ }
+ if (scbid >= ahd->scb_data.numscbs) {
+ printf("%s: Waiting TID List inconsistency. "
+ "SCB index == 0x%x, yet numscbs == 0x%x.",
+ ahd_name(ahd), scbid, ahd->scb_data.numscbs);
+ ahd_dump_card_state(ahd);
+ panic("for safety");
+ }
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: SCB = 0x%x Not Active!\n",
+ ahd_name(ahd), scbid);
+ panic("Waiting TID List traversal\n");
+ }
+ ahd_set_scbptr(ahd, scbid);
+ tid_next = ahd_inw_scbram(ahd, SCB_NEXT2);
+ if (ahd_match_scb(ahd, scb, target, channel, CAM_LUN_WILDCARD,
+ SCB_LIST_NULL, ROLE_UNKNOWN) == 0) {
+ tid_prev = scbid;
+ continue;
+ }
+
+ /*
+ * We found a list of scbs that needs to be searched.
+ */
+ if (action == SEARCH_PRINT)
+ printf(" %d ( ", SCB_GET_TARGET(ahd, scb));
+ tid_head = scbid;
+ found += ahd_search_scb_list(ahd, target, channel,
+ lun, tag, role, status,
+ action, &tid_head,
+ SCB_GET_TARGET(ahd, scb));
+ if (tid_head != scbid)
+ ahd_stitch_tid_list(ahd, tid_prev, tid_head, tid_next);
+ if (!SCBID_IS_NULL(tid_head))
+ tid_prev = tid_head;
+ if (action == SEARCH_PRINT)
+ printf(")\n");
+ }
+ ahd_set_scbptr(ahd, savedscbptr);
+
+ if (action == SEARCH_COMPLETE)
+ ahd_release_untagged_queues(ahd);
+ ahd_restore_modes(ahd, saved_modes);
+ return (found);
+}
+
+static int
+ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status,
+ ahd_search_action action, u_int *list_head, u_int tid)
+{
+ struct scb *scb;
+ u_int scbid;
+ u_int next;
+ u_int prev;
+ int found;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ found = 0;
+ prev = SCB_LIST_NULL;
+ next = *list_head;
+ for (scbid = next; !SCBID_IS_NULL(scbid); scbid = next) {
+ if (scbid >= ahd->scb_data.numscbs) {
+ printf("%s:SCB List inconsistency. "
+ "SCB == 0x%x, yet numscbs == 0x%x.",
+ ahd_name(ahd), scbid, ahd->scb_data.numscbs);
+ ahd_dump_card_state(ahd);
+ panic("for safety");
+ }
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: SCB = %d Not Active!\n",
+ ahd_name(ahd), scbid);
+ panic("Waiting List traversal\n");
+ }
+ ahd_set_scbptr(ahd, scbid);
+ next = ahd_inw_scbram(ahd, SCB_NEXT);
+ if (ahd_match_scb(ahd, scb, target, channel,
+ lun, SCB_LIST_NULL, role) == 0) {
+ prev = scbid;
+ continue;
+ }
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahd_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahd_set_transaction_status(scb, status);
+ cstat = ahd_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahd_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in Waiting List\n");
+ ahd_done(ahd, scb);
+ /* FALLTHROUGH */
+ }
+ case SEARCH_REMOVE:
+ ahd_rem_wscb(ahd, scbid, prev, next, tid);
+ if (prev == SCB_LIST_NULL)
+ *list_head = next;
+ break;
+ case SEARCH_PRINT:
+ printf("0x%x ", scbid);
+ case SEARCH_COUNT:
+ prev = scbid;
+ break;
+ }
+ if (found > AHD_SCB_MAX)
+ panic("SCB LIST LOOP");
+ }
+ return (found);
+}
+
+static void
+ahd_stitch_tid_list(struct ahd_softc *ahd, u_int tid_prev,
+ u_int tid_cur, u_int tid_next)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+
+ if (SCBID_IS_NULL(tid_cur)) {
+
+ /* Bypass current TID list */
+ if (SCBID_IS_NULL(tid_prev)) {
+ ahd_outw(ahd, WAITING_TID_HEAD, tid_next);
+ } else {
+ ahd_set_scbptr(ahd, tid_prev);
+ ahd_outw(ahd, SCB_NEXT2, tid_next);
+ }
+ if (SCBID_IS_NULL(tid_next))
+ ahd_outw(ahd, WAITING_TID_TAIL, tid_prev);
+ } else {
+
+ /* Stitch through tid_cur */
+ if (SCBID_IS_NULL(tid_prev)) {
+ ahd_outw(ahd, WAITING_TID_HEAD, tid_cur);
+ } else {
+ ahd_set_scbptr(ahd, tid_prev);
+ ahd_outw(ahd, SCB_NEXT2, tid_cur);
+ }
+ ahd_set_scbptr(ahd, tid_cur);
+ ahd_outw(ahd, SCB_NEXT2, tid_next);
+
+ if (SCBID_IS_NULL(tid_next))
+ ahd_outw(ahd, WAITING_TID_TAIL, tid_cur);
+ }
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
+ u_int prev, u_int next, u_int tid)
+{
+ u_int tail_offset;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ if (!SCBID_IS_NULL(prev)) {
+ ahd_set_scbptr(ahd, prev);
+ ahd_outw(ahd, SCB_NEXT, next);
+ }
+
+ /*
+ * SCBs that had MK_MESSAGE set in them will not
+ * be queued to the per-target lists, so don't
+ * blindly clear the tail pointer.
+ */
+ tail_offset = WAITING_SCB_TAILS + (2 * tid);
+ if (SCBID_IS_NULL(next)
+ && ahd_inw(ahd, tail_offset) == scbid)
+ ahd_outw(ahd, tail_offset, prev);
+ ahd_add_scb_to_free_list(ahd, scbid);
+ return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs. This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahd_add_scb_to_free_list(struct ahd_softc *ahd, u_int scbid)
+{
+/* XXX Need some other mechanism to designate "free". */
+ /*
+ * Invalidate the tag so that our abort
+ * routines don't think it's active.
+ ahd_outb(ahd, SCB_TAG, SCB_LIST_NULL);
+ */
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG. This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+int
+ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status)
+{
+ struct scb *scbp;
+ struct scb *scbp_next;
+ u_int active_scb;
+ u_int i, j;
+ u_int maxtarget;
+ u_int minlun;
+ u_int maxlun;
+
+ int found;
+
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahd_freeze_untagged_queues(ahd);
+
+ /* restore this when we're done */
+ active_scb = ahd_get_scbptr(ahd);
+
+ found = ahd_search_qinfifo(ahd, target, channel, lun, SCB_LIST_NULL,
+ role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ /*
+ * Clean out the busy target table for any untagged commands.
+ */
+ i = 0;
+ maxtarget = 16;
+ if (target != CAM_TARGET_WILDCARD) {
+ i = target;
+ if (channel == 'B')
+ i += 8;
+ maxtarget = i + 1;
+ }
+
+ if (lun == CAM_LUN_WILDCARD) {
+ minlun = 0;
+ maxlun = AHD_NUM_LUNS_NONPKT;
+ } else if (lun >= AHD_NUM_LUNS_NONPKT) {
+ minlun = maxlun = 0;
+ } else {
+ minlun = lun;
+ maxlun = lun + 1;
+ }
+
+ if (role != ROLE_TARGET) {
+ for (;i < maxtarget; i++) {
+ for (j = minlun;j < maxlun; j++) {
+ u_int scbid;
+ u_int tcl;
+
+ tcl = BUILD_TCL(i << 4, j);
+ scbid = ahd_find_busy_tcl(ahd, tcl);
+ scbp = ahd_lookup_scb(ahd, scbid);
+ if (scbp == NULL
+ || ahd_match_scb(ahd, scbp, target, channel,
+ lun, tag, role) == 0)
+ continue;
+ ahd_unbusy_tcl(ahd, BUILD_TCL_RAW(i, 'A', j));
+ }
+ }
+ }
+
+ /*
+ * Go through the pending CCB list and look for
+ * commands for this target that are still active.
+ * These are other tagged commands that were
+ * disconnected when the reset occurred.
+ */
+ scbp_next = LIST_FIRST(&ahd->pending_scbs);
+ while (scbp_next != NULL) {
+ scbp = scbp_next;
+ scbp_next = LIST_NEXT(scbp, pending_links);
+ if (ahd_match_scb(ahd, scbp, target, channel, lun, tag, role)) {
+ cam_status ostat;
+
+ ostat = ahd_get_transaction_status(scbp);
+ if (ostat == CAM_REQ_INPROG)
+ ahd_set_transaction_status(scbp, status);
+ if (ahd_get_transaction_status(scbp) != CAM_REQ_CMP)
+ ahd_freeze_scb(scbp);
+ if ((scbp->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB on pending list\n");
+ ahd_done(ahd, scbp);
+ found++;
+ }
+ }
+ ahd_set_scbptr(ahd, active_scb);
+ ahd_platform_abort_scbs(ahd, target, channel, lun, tag, role, status);
+ ahd_release_untagged_queues(ahd);
+ return found;
+}
+
+static void
+ahd_reset_current_bus(struct ahd_softc *ahd)
+{
+ uint8_t scsiseq;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) & ~ENSCSIRST);
+ scsiseq = ahd_inb(ahd, SCSISEQ0);
+ ahd_outb(ahd, SCSISEQ0, scsiseq | SCSIRSTO);
+ ahd_delay(AHD_BUSRESET_DELAY);
+ /* Turn off the bus reset */
+ ahd_outb(ahd, SCSISEQ0, scsiseq & ~SCSIRSTO);
+ if ((ahd->bugs & AHD_SCSIRST_BUG) != 0) {
+ /*
+ * 2A Razor #474
+ * Certain chip state is not cleared for
+ * SCSI bus resets that we initiate, so
+ * we must reset the chip.
+ */
+ ahd_reset(ahd);
+ ahd_intr_enable(ahd, /*enable*/TRUE);
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ }
+
+ ahd_clear_intstat(ahd);
+
+ /* Re-enable reset interrupts */
+ ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
+}
+
+int
+ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
+{
+ struct ahd_devinfo devinfo;
+#if AHD_TARGET_MODE
+ u_int target;
+ u_int max_scsiid;
+#endif
+ int found;
+
+ ahd->pending_device = NULL;
+
+ ahd_compile_devinfo(&devinfo,
+ CAM_TARGET_WILDCARD,
+ CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD,
+ channel, ROLE_UNKNOWN);
+ ahd_pause(ahd);
+
+ /* Make sure the sequencer is in a safe location. */
+ ahd_clear_critical_section(ahd);
+
+ /*
+ * Run our command complete fifos to ensure that we perform
+ * completion processing on any commands that 'completed'
+ * before the reset occurred.
+ */
+ ahd_run_qoutfifo(ahd);
+#if AHD_TARGET_MODE
+ if ((ahd->flags & AHD_TARGETROLE) != 0) {
+ ahd_run_tqinfifo(ahd, /*paused*/TRUE);
+ }
+#endif
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ /*
+ * Reset the bus if we are initiating this reset
+ */
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) & ~(ENBUSFREE|ENSCSIRST));
+ if (initiate_reset)
+ ahd_reset_current_bus(ahd);
+ ahd_clear_intstat(ahd);
+
+ /*
+ * Clean up all the state information for the
+ * pending transactions on this bus.
+ */
+ found = ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, channel,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+#ifdef AHD_TARGET_MODE
+ max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
+
+ /*
+ * Send an immediate notify ccb to all target more peripheral
+ * drivers affected by this action.
+ */
+ for (target = 0; target <= max_scsiid; target++) {
+ struct ahd_tmode_tstate* tstate;
+ u_int lun;
+
+ tstate = ahd->enabled_targets[target];
+ if (tstate == NULL)
+ continue;
+ for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+ struct ahd_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[lun];
+ if (lstate == NULL)
+ continue;
+
+ ahd_queue_lstate_event(ahd, lstate, CAM_TARGET_WILDCARD,
+ EVENT_TYPE_BUS_RESET, /*arg*/0);
+ ahd_send_lstate_events(ahd, lstate);
+ }
+ }
+#endif
+ /* Notify the XPT that a bus reset occurred */
+ ahd_send_async(ahd, devinfo.channel, CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
+
+ /*
+ * Freeze the SIMQ until our poller can determine that
+ * the bus reset has really gone away. We set the initial
+ * timer to 0 to have the check performed as soon as possible
+ * from the timer context.
+ */
+ ahd_freeze_simq(ahd);
+ ahd_timer_reset(&ahd->reset_timer, 0, ahd_reset_poll, ahd);
+ return (found);
+}
+
+
+#define AHD_RESET_POLL_US 1000
+static void
+ahd_reset_poll(void *arg)
+{
+ struct ahd_softc *ahd;
+ u_int scsiseq1;
+ u_int initiator, target, max_scsiid;
+ u_long l;
+ u_long s;
+
+ ahd_list_lock(&l);
+ ahd = ahd_find_softc((struct ahd_softc *)arg);
+ if (ahd == NULL) {
+ printf("ahd_reset_poll: Instance %p no longer exists\n", arg);
+ ahd_list_unlock(&l);
+ return;
+ }
+ ahd_lock(ahd, &s);
+ if (ahd_is_paused(ahd) == 0)
+ panic("Someone unpaused the sequencer!\n");
+
+ ahd_clear_intstat(ahd);
+ if ((ahd_inb(ahd, SSTAT0) & SCSIRSTI) != 0) {
+ ahd_timer_reset(&ahd->reset_timer, AHD_RESET_POLL_US,
+ ahd_reset_poll, ahd);
+ ahd_unlock(ahd, &s);
+ }
+
+ /* Reset is now low. Complete chip reinitialization. */
+ ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
+ scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+ ahd_outb(ahd, SCSISEQ1, scsiseq1 & (ENSELI|ENRSELI|ENAUTOATNP));
+
+ /*
+ * Revert to async/narrow transfers until we renegotiate.
+ */
+ max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
+ for (target = 0; target <= max_scsiid; target++) {
+
+ if (ahd->enabled_targets[target] == NULL)
+ continue;
+ for (initiator = 0; initiator <= max_scsiid; initiator++) {
+ struct ahd_devinfo devinfo;
+
+ ahd_compile_devinfo(&devinfo, target, initiator,
+ CAM_LUN_WILDCARD,
+ 'A', ROLE_UNKNOWN);
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR, /*paused*/TRUE);
+ ahd_set_syncrate(ahd, &devinfo, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHD_TRANS_CUR, /*paused*/TRUE);
+ }
+ }
+
+ ahd_clear_fifo(ahd, 0);
+ ahd_clear_fifo(ahd, 1);
+ ahd_restart(ahd);
+ ahd_unlock(ahd, &s);
+ ahd_release_simq(ahd);
+ ahd_list_unlock(&l);
+}
+
+
+/****************************** Status Processing *****************************/
+void
+ahd_handle_scb_status(struct ahd_softc *ahd, struct scb *scb)
+{
+ if (scb->hscb->shared_data.istatus.scsi_status != 0) {
+ ahd_handle_scsi_status(ahd, scb);
+ } else {
+ ahd_calc_residual(ahd, scb);
+ ahd_done(ahd, scb);
+ }
+}
+
+void
+ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ u_int qfreeze_cnt;
+ ahd_mode_state saved_modes;
+
+ /*
+ * The sequencer freezes its select-out queue
+ * anytime a SCSI status error occurs. We must
+ * handle the error and decrement the QFREEZE count
+ * to allow the sequencer to continue.
+ */
+ hscb = scb->hscb;
+
+ /* Don't want to clobber the original sense code */
+ if ((scb->flags & SCB_SENSE) != 0) {
+ /*
+ * Clear the SCB_SENSE Flag and perform
+ * a normal command completion.
+ */
+ scb->flags &= ~SCB_SENSE;
+ ahd_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ ahd_done(ahd, scb);
+ return;
+ }
+ ahd_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+ /* Freeze the queue until the client sees the error. */
+ ahd_pause(ahd);
+ saved_modes = ahd_save_modes(ahd);
+ ahd_clear_critical_section(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_freeze_devq(ahd, scb);
+ ahd_freeze_scb(scb);
+ qfreeze_cnt = ahd_inw(ahd, QFREEZE_COUNT);
+ if (qfreeze_cnt == 0) {
+ printf("%s: Bad status with 0 qfreeze count!\n", ahd_name(ahd));
+ } else {
+ qfreeze_cnt--;
+ ahd_outw(ahd, QFREEZE_COUNT, qfreeze_cnt);
+ }
+ if (qfreeze_cnt == 0)
+ ahd_outb(ahd, SEQ_FLAGS2,
+ ahd_inb(ahd, SEQ_FLAGS2) & ~SELECTOUT_QFROZEN);
+ ahd_unpause(ahd);
+ ahd_set_scsi_status(scb, hscb->shared_data.istatus.scsi_status);
+ switch (hscb->shared_data.istatus.scsi_status) {
+ case STATUS_PKT_SENSE:
+ {
+ struct scsi_status_iu_header *siu;
+
+ ahd_sync_sense(ahd, scb, BUS_DMASYNC_POSTREAD);
+ siu = (struct scsi_status_iu_header *)scb->sense_data;
+ ahd_set_scsi_status(scb, siu->status);
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_SENSE) != 0)
+ ahd_print_path(ahd, scb);
+ printf("SCB 0x%x Received PKT Status of 0x%x\n",
+ SCB_GET_TAG(scb), siu->status);
+ printf("\tflags = 0x%x, sense len = 0x%x, "
+ "pktfail = 0x%x\n",
+ siu->flags, scsi_4btoul(siu->sense_length),
+ scsi_4btoul(siu->pkt_failures_length));
+#endif
+ if ((siu->flags & SIU_RSPVALID) != 0) {
+ ahd_print_path(ahd, scb);
+ if (scsi_4btoul(siu->pkt_failures_length) < 4) {
+ printf("Unable to parse pkt_failures\n");
+ } else {
+
+ switch (SIU_PKTFAIL_CODE(siu)) {
+ case SIU_PFC_NONE:
+ printf("No packet failure found\n");
+ break;
+ case SIU_PFC_CIU_FIELDS_INVALID:
+ printf("Invalid Command IU Field\n");
+ break;
+ case SIU_PFC_TMF_NOT_SUPPORTED:
+ printf("TMF not supportd\n");
+ break;
+ case SIU_PFC_TMF_FAILED:
+ printf("TMF failed\n");
+ break;
+ case SIU_PFC_INVALID_TYPE_CODE:
+ printf("Invalid L_Q Type code\n");
+ break;
+ case SIU_PFC_ILLEGAL_REQUEST:
+ printf("Illegal request\n");
+ default:
+ break;
+ }
+ }
+ if (siu->status == SCSI_STATUS_OK)
+ ahd_set_transaction_status(scb,
+ CAM_REQ_CMP_ERR);
+ }
+ if ((siu->flags & SIU_SNSVALID) != 0) {
+ scb->flags |= SCB_PKT_SENSE;
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_SENSE) != 0)
+ printf("Sense data available\n");
+#endif
+ }
+ ahd_done(ahd, scb);
+ break;
+ }
+ case SCSI_STATUS_CMD_TERMINATED:
+ case SCSI_STATUS_CHECK_COND:
+ {
+ struct ahd_devinfo devinfo;
+ struct ahd_dma_seg *sg;
+ struct scsi_sense *sc;
+ struct ahd_initiator_tinfo *targ_info;
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_transinfo *tinfo;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_SENSE) {
+ ahd_print_path(ahd, scb);
+ printf("SCB %d: requests Check Status\n",
+ SCB_GET_TAG(scb));
+ }
+#endif
+
+ if (ahd_perform_autosense(scb) == 0)
+ break;
+
+ ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb),
+ SCB_GET_TARGET(ahd, scb),
+ SCB_GET_LUN(scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ ROLE_INITIATOR);
+ targ_info = ahd_fetch_transinfo(ahd,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo = &targ_info->curr;
+ sg = scb->sg_list;
+ sc = (struct scsi_sense *)hscb->shared_data.idata.cdb;
+ /*
+ * Save off the residual if there is one.
+ */
+ ahd_update_residual(ahd, scb);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_SENSE) {
+ ahd_print_path(ahd, scb);
+ printf("Sending Sense\n");
+ }
+#endif
+ scb->sg_count = 0;
+ sg = ahd_sg_setup(ahd, scb, sg, ahd_get_sense_bufaddr(ahd, scb),
+ ahd_get_sense_bufsize(ahd, scb),
+ /*last*/TRUE);
+ sc->opcode = REQUEST_SENSE;
+ sc->byte2 = 0;
+ if (tinfo->protocol_version <= SCSI_REV_2
+ && SCB_GET_LUN(scb) < 8)
+ sc->byte2 = SCB_GET_LUN(scb) << 5;
+ sc->unused[0] = 0;
+ sc->unused[1] = 0;
+ sc->length = ahd_get_sense_bufsize(ahd, scb);
+ sc->control = 0;
+
+ /*
+ * We can't allow the target to disconnect.
+ * This will be an untagged transaction and
+ * having the target disconnect will make this
+ * transaction indestinguishable from outstanding
+ * tagged transactions.
+ */
+ hscb->control = 0;
+
+ /*
+ * This request sense could be because the
+ * the device lost power or in some other
+ * way has lost our transfer negotiations.
+ * Renegotiate if appropriate. Unit attention
+ * errors will be reported before any data
+ * phases occur.
+ */
+ if (ahd_get_residual(scb) == ahd_get_transfer_length(scb)) {
+ ahd_update_neg_request(ahd, &devinfo,
+ tstate, targ_info,
+ /*force*/TRUE);
+ }
+ if (tstate->auto_negotiate & devinfo.target_mask) {
+ hscb->control |= MK_MESSAGE;
+ scb->flags &=
+ ~(SCB_NEGOTIATE|SCB_ABORT|SCB_DEVICE_RESET);
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ }
+ hscb->cdb_len = sizeof(*sc);
+ ahd_setup_data_scb(ahd, scb);
+ scb->flags |= SCB_SENSE;
+ ahd_queue_scb(ahd, scb);
+#ifdef __FreeBSD__
+ /*
+ * Ensure we have enough time to actually
+ * retrieve the sense.
+ */
+ untimeout(ahd_timeout, (caddr_t)scb,
+ scb->io_ctx->ccb_h.timeout_ch);
+ scb->io_ctx->ccb_h.timeout_ch =
+ timeout(ahd_timeout, (caddr_t)scb, 5 * hz);
+#endif
+ break;
+ }
+ case SCSI_STATUS_OK:
+ printf("%s: Interrupted for staus of 0???\n",
+ ahd_name(ahd));
+ /* FALLTHROUGH */
+ default:
+ ahd_done(ahd, scb);
+ break;
+ }
+}
+
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+void
+ahd_calc_residual(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ struct initiator_status *spkt;
+ uint32_t sgptr;
+ uint32_t resid_sgptr;
+ uint32_t resid;
+
+ /*
+ * 5 cases.
+ * 1) No residual.
+ * SG_STATUS_VALID clear in sgptr.
+ * 2) Transferless command
+ * 3) Never performed any transfers.
+ * sgptr has SG_FULL_RESID set.
+ * 4) No residual but target did not
+ * save data pointers after the
+ * last transfer, so sgptr was
+ * never updated.
+ * 5) We have a partial residual.
+ * Use residual_sgptr to determine
+ * where we are.
+ */
+
+ hscb = scb->hscb;
+ sgptr = ahd_le32toh(hscb->sgptr);
+ if ((sgptr & SG_STATUS_VALID) == 0)
+ /* Case 1 */
+ return;
+ sgptr &= ~SG_STATUS_VALID;
+
+ if ((sgptr & SG_LIST_NULL) != 0)
+ /* Case 2 */
+ return;
+
+ /*
+ * Residual fields are the same in both
+ * target and initiator status packets,
+ * so we can always use the initiator fields
+ * regardless of the role for this SCB.
+ */
+ spkt = &hscb->shared_data.istatus;
+ resid_sgptr = ahd_le32toh(spkt->residual_sgptr);
+ if ((sgptr & SG_FULL_RESID) != 0) {
+ /* Case 3 */
+ resid = ahd_get_transfer_length(scb);
+ } else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+ /* Case 4 */
+ return;
+ } else if ((resid_sgptr & SG_OVERRUN_RESID) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("data overrun detected Tag == 0x%x.\n",
+ SCB_GET_TAG(scb));
+ ahd_freeze_devq(ahd, scb);
+ ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ ahd_freeze_scb(scb);
+ return;
+ } else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+ panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+ /* NOTREACHED */
+ } else {
+ struct ahd_dma_seg *sg;
+
+ /*
+ * Remainder of the SG where the transfer
+ * stopped.
+ */
+ resid = ahd_le32toh(spkt->residual_datacnt) & AHD_SG_LEN_MASK;
+ sg = ahd_sg_bus_to_virt(ahd, scb, resid_sgptr & SG_PTR_MASK);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ /*
+ * Add up the contents of all residual
+ * SG segments that are after the SG where
+ * the transfer stopped.
+ */
+ while ((ahd_le32toh(sg->len) & AHD_DMA_LAST_SEG) == 0) {
+ sg++;
+ resid += ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+ }
+ }
+ if ((scb->flags & SCB_SENSE) == 0)
+ ahd_set_residual(scb, resid);
+ else
+ ahd_set_sense_residual(scb, resid);
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Handled Residual of %d bytes\n", resid);
+ }
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHD_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahd_queue_lstate_event(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate,
+ u_int initiator_id, u_int event_type, u_int event_arg)
+{
+ struct ahd_tmode_event *event;
+ int pending;
+
+ xpt_freeze_devq(lstate->path, /*count*/1);
+ if (lstate->event_w_idx >= lstate->event_r_idx)
+ pending = lstate->event_w_idx - lstate->event_r_idx;
+ else
+ pending = AHD_TMODE_EVENT_BUFFER_SIZE + 1
+ - (lstate->event_r_idx - lstate->event_w_idx);
+
+ if (event_type == EVENT_TYPE_BUS_RESET
+ || event_type == MSG_BUS_DEV_RESET) {
+ /*
+ * Any earlier events are irrelevant, so reset our buffer.
+ * This has the effect of allowing us to deal with reset
+ * floods (an external device holding down the reset line)
+ * without losing the event that is really interesting.
+ */
+ lstate->event_r_idx = 0;
+ lstate->event_w_idx = 0;
+ xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+ }
+
+ if (pending == AHD_TMODE_EVENT_BUFFER_SIZE) {
+ xpt_print_path(lstate->path);
+ printf("immediate event %x:%x lost\n",
+ lstate->event_buffer[lstate->event_r_idx].event_type,
+ lstate->event_buffer[lstate->event_r_idx].event_arg);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+ }
+
+ event = &lstate->event_buffer[lstate->event_w_idx];
+ event->initiator_id = initiator_id;
+ event->event_type = event_type;
+ event->event_arg = event_arg;
+ lstate->event_w_idx++;
+ if (lstate->event_w_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahd_send_lstate_events(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate)
+{
+ struct ccb_hdr *ccbh;
+ struct ccb_immed_notify *inot;
+
+ while (lstate->event_r_idx != lstate->event_w_idx
+ && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+ struct ahd_tmode_event *event;
+
+ event = &lstate->event_buffer[lstate->event_r_idx];
+ SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+ inot = (struct ccb_immed_notify *)ccbh;
+ switch (event->event_type) {
+ case EVENT_TYPE_BUS_RESET:
+ ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+ break;
+ default:
+ ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+ inot->message_args[0] = event->event_type;
+ inot->message_args[1] = event->event_arg;
+ break;
+ }
+ inot->initiator_id = event->initiator_id;
+ inot->sense_len = 0;
+ xpt_done((union ccb *)inot);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ }
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHD_DUMP_SEQ
+void
+ahd_dumpseq(struct ahd_softc* ahd)
+{
+ int i;
+ int max_prog;
+
+ max_prog = 2048;
+
+ ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahd_outb(ahd, PRGMCNT, 0);
+ ahd_outb(ahd, PRGMCNT+1, 0);
+ for (i = 0; i < max_prog; i++) {
+ uint8_t ins_bytes[4];
+
+ ahd_insb(ahd, SEQRAM, ins_bytes, 4);
+ printf("0x%08x\n", ins_bytes[0] << 24
+ | ins_bytes[1] << 16
+ | ins_bytes[2] << 8
+ | ins_bytes[3]);
+ }
+}
+#endif
+
+static void
+ahd_loadseq(struct ahd_softc *ahd)
+{
+ struct cs cs_table[num_critical_sections];
+ u_int begin_set[num_critical_sections];
+ u_int end_set[num_critical_sections];
+ struct patch *cur_patch;
+ u_int cs_count;
+ u_int cur_cs;
+ u_int i;
+ int downloaded;
+ u_int skip_addr;
+ u_int sg_prefetch_cnt;
+ u_int sg_prefetch_cnt_limit;
+ u_int sg_prefetch_align;
+ u_int sg_size;
+ uint8_t download_consts[DOWNLOAD_CONST_COUNT];
+
+ if (bootverbose)
+ printf("%s: Downloading Sequencer Program...",
+ ahd_name(ahd));
+
+#if DOWNLOAD_CONST_COUNT != 6
+#error "Download Const Mismatch"
+#endif
+ /*
+ * Start out with 0 critical sections
+ * that apply to this firmware load.
+ */
+ cs_count = 0;
+ cur_cs = 0;
+ memset(begin_set, 0, sizeof(begin_set));
+ memset(end_set, 0, sizeof(end_set));
+
+ /*
+ * Setup downloadable constant table.
+ *
+ * The computation for the S/G prefetch variables is
+ * a bit complicated. We would like to always fetch
+ * in terms of cachelined sized increments. However,
+ * if the cacheline is not an even multiple of the
+ * SG element size or is larger than our SG RAM, using
+ * just the cache size might leave us with only a portion
+ * of an SG element at the tail of a prefetch. If the
+ * cacheline is larger than our S/G prefetch buffer less
+ * the size of an SG element, we may round down to a cacheline
+ * that doesn't contain any or all of the S/G of interest
+ * within the bounds of our S/G ram. Provide variables to
+ * the sequencer that will allow it to handle these edge
+ * cases.
+ */
+ /* Start by aligning to the nearest cacheline. */
+ sg_prefetch_align = ahd->pci_cachesize;
+ if (sg_prefetch_align == 0)
+ sg_prefetch_cnt = 8;
+ /* Round down to the nearest power of 2. */
+ while (powerof2(sg_prefetch_align) == 0)
+ sg_prefetch_align--;
+ /*
+ * If the cacheline boundary is greater than half our prefetch RAM
+ * we risk not being able to fetch even a single complete S/G
+ * segment if we align to that boundary.
+ */
+ if (sg_prefetch_align > CCSGADDR_MAX/2)
+ sg_prefetch_align = CCSGADDR_MAX/2;
+ /* Start by fetching a single cacheline. */
+ sg_prefetch_cnt = sg_prefetch_align;
+ /*
+ * Increment the prefetch count by cachelines until
+ * at least one S/G element will fit.
+ */
+ sg_size = sizeof(struct ahd_dma_seg);
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+ sg_size = sizeof(struct ahd_dma64_seg);
+ while (sg_prefetch_cnt < sg_size)
+ sg_prefetch_cnt += sg_prefetch_align;
+ /*
+ * If the cacheline is not an even multiple of
+ * the S/G size, we may only get a partial S/G when
+ * we align. Add a cacheline if this is the case.
+ */
+ if ((sg_prefetch_align % sg_size) != 0
+ && (sg_prefetch_cnt < CCSGADDR_MAX))
+ sg_prefetch_cnt += sg_prefetch_align;
+ /*
+ * Lastly, compute a value that the sequencer can use
+ * to determine if the remainder of the CCSGRAM buffer
+ * has a full S/G element in it.
+ */
+ sg_prefetch_cnt_limit = -(sg_prefetch_cnt - sg_size + 1);
+ download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+ download_consts[SG_PREFETCH_CNT_LIMIT] = sg_prefetch_cnt_limit;
+ download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_align - 1);
+ download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_align - 1);
+ download_consts[SG_SIZEOF] = sg_size;
+ download_consts[PKT_OVERRUN_BUFOFFSET] =
+ (AHD_SCB_MAX * sizeof(uint16_t)) / 256;
+ if ((ahd->features & AHD_TARGETMODE) != 0)
+ download_consts[PKT_OVERRUN_BUFOFFSET] +=
+ (AHD_TMODE_CMDS * sizeof(struct target_cmd)) / 256;
+
+ cur_patch = patches;
+ downloaded = 0;
+ skip_addr = 0;
+ ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahd_outb(ahd, PRGMCNT, 0);
+ ahd_outb(ahd, PRGMCNT+1, 0);
+
+ for (i = 0; i < sizeof(seqprog)/4; i++) {
+ if (ahd_check_patch(ahd, &cur_patch, i, &skip_addr) == 0) {
+ /*
+ * Don't download this instruction as it
+ * is in a patch that was removed.
+ */
+ continue;
+ }
+ /*
+ * Move through the CS table until we find a CS
+ * that might apply to this instruction.
+ */
+ for (; cur_cs < num_critical_sections; cur_cs++) {
+ if (critical_sections[cur_cs].end <= i) {
+ if (begin_set[cs_count] == TRUE
+ && end_set[cs_count] == FALSE) {
+ cs_table[cs_count].end = downloaded;
+ end_set[cs_count] = TRUE;
+ cs_count++;
+ }
+ continue;
+ }
+ if (critical_sections[cur_cs].begin <= i
+ && begin_set[cs_count] == FALSE) {
+ cs_table[cs_count].begin = downloaded;
+ begin_set[cs_count] = TRUE;
+ }
+ break;
+ }
+ ahd_download_instr(ahd, i, download_consts);
+ downloaded++;
+ }
+
+ ahd->num_critical_sections = cs_count;
+ if (cs_count != 0) {
+
+ cs_count *= sizeof(struct cs);
+ ahd->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
+ if (ahd->critical_sections == NULL)
+ panic("ahd_loadseq: Could not malloc");
+ memcpy(ahd->critical_sections, cs_table, cs_count);
+ }
+ ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE);
+
+ if (bootverbose)
+ printf(" %d instructions downloaded\n", downloaded);
+}
+
+static int
+ahd_check_patch(struct ahd_softc *ahd, struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr)
+{
+ struct patch *cur_patch;
+ struct patch *last_patch;
+ u_int num_patches;
+
+ num_patches = sizeof(patches)/sizeof(struct patch);
+ last_patch = &patches[num_patches];
+ cur_patch = *start_patch;
+
+ while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+ if (cur_patch->patch_func(ahd) == 0) {
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ cur_patch += cur_patch->skip_patch;
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
+ */
+ cur_patch++;
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
+}
+
+static u_int
+ahd_resolve_seqaddr(struct ahd_softc *ahd, u_int address)
+{
+ struct patch *cur_patch;
+ int address_offset;
+ u_int skip_addr;
+ u_int i;
+
+ address_offset = 0;
+ cur_patch = patches;
+ skip_addr = 0;
+
+ for (i = 0; i < address;) {
+
+ ahd_check_patch(ahd, &cur_patch, i, &skip_addr);
+
+ if (skip_addr > i) {
+ int end_addr;
+
+ end_addr = MIN(address, skip_addr);
+ address_offset += end_addr - i;
+ i = skip_addr;
+ } else {
+ i++;
+ }
+ }
+ return (address - address_offset);
+}
+
+static void
+ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts)
+{
+ union ins_formats instr;
+ struct ins_format1 *fmt1_ins;
+ struct ins_format3 *fmt3_ins;
+ u_int opcode;
+
+ /*
+ * The firmware is always compiled into a little endian format.
+ */
+ instr.integer = ahd_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+ fmt1_ins = &instr.format1;
+ fmt3_ins = NULL;
+
+ /* Pull the opcode */
+ opcode = instr.format1.opcode;
+ switch (opcode) {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ fmt3_ins = &instr.format3;
+ fmt3_ins->address = ahd_resolve_seqaddr(ahd, fmt3_ins->address);
+ /* FALLTHROUGH */
+ }
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_BMOV:
+ if (fmt1_ins->parity != 0) {
+ fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+ }
+ fmt1_ins->parity = 0;
+ /* FALLTHROUGH */
+ case AIC_OP_ROL:
+ {
+ int i, count;
+
+ /* Calculate odd parity for the instruction */
+ for (i = 0, count = 0; i < 31; i++) {
+ uint32_t mask;
+
+ mask = 0x01 << i;
+ if ((instr.integer & mask) != 0)
+ count++;
+ }
+ if ((count & 0x01) == 0)
+ instr.format1.parity = 1;
+
+ /* The sequencer is a little endian cpu */
+ instr.integer = ahd_htole32(instr.integer);
+ ahd_outsb(ahd, SEQRAM, instr.bytes, 4);
+ break;
+ }
+ default:
+ panic("Unknown opcode encountered in seq program");
+ break;
+ }
+}
+
+void
+ahd_dump_all_cards_state()
+{
+ struct ahd_softc *list_ahd;
+
+ TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+ ahd_dump_card_state(list_ahd);
+ }
+}
+
+void
+ahd_dump_card_state(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ ahd_mode_state saved_modes;
+ u_int dffstat;
+ int paused;
+ u_int scb_index;
+ u_int i;
+
+ if (ahd_is_paused(ahd)) {
+ paused = 1;
+ } else {
+ paused = 0;
+ ahd_pause(ahd);
+ }
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ printf("%s: Dumping Card State at program address 0x%x Mode 0x%x\n",
+ ahd_name(ahd),
+ ahd_inb(ahd, CURADDR) | (ahd_inb(ahd, CURADDR+1) << 8),
+ ahd_build_mode_state(ahd, ahd->saved_src_mode,
+ ahd->saved_dst_mode));
+ printf("Softc pointer is %p\n", ahd);
+ printf("IOWNID == 0x%x, TOWNID == 0x%x, SCSISEQ1 == 0x%x\n",
+ ahd_inb(ahd, IOWNID), ahd_inb(ahd, TOWNID),
+ ahd_inb(ahd, SCSISEQ1));
+ printf("SCSISIGI == 0x%x\n", ahd_inb(ahd, SCSISIGI));
+ printf("QFREEZE_COUNT == %d, SEQ_FLAGS2 == 0x%x\n",
+ ahd_inw(ahd, QFREEZE_COUNT), ahd_inb(ahd, SEQ_FLAGS2));
+ if (paused)
+ printf("Card was paused\n");
+ printf("%s: LASTSCB 0x%x CURRSCB 0x%x NEXTSCB 0x%x SEQINTCTL 0x%x\n",
+ ahd_name(ahd), ahd_inw(ahd, LASTSCB), ahd_inw(ahd, CURRSCB),
+ ahd_inw(ahd, NEXTSCB), ahd_inb(ahd, SEQINTCTL));
+ printf("SCSISEQ = 0x%x\n", ahd_inb(ahd, SCSISEQ0));
+ printf("SCB count = %d\n", ahd->scb_data.numscbs);
+ printf("Kernel NEXTQSCB = %d\n", SCB_GET_TAG(ahd->next_queued_scb));
+ printf("%s: LQCTL1 = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, LQCTL1));
+ printf("%s: WAITING_TID_LIST == 0x%x:0x%x\n",
+ ahd_name(ahd), ahd_inw(ahd, WAITING_TID_HEAD),
+ ahd_inw(ahd, WAITING_TID_TAIL));
+ printf("%s: WAITING_SCB_TAILS: ", ahd_name(ahd));
+ for (i = 0; i < AHD_NUM_TARGETS; i++) {
+ printf("%d(0x%x) ", i,
+ ahd_inw(ahd, WAITING_SCB_TAILS + (2 * i)));
+ }
+ printf("\n");
+ /* QINFIFO */
+ ahd_search_qinfifo(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_UNKNOWN, /*status*/0, SEARCH_PRINT);
+ printf("Pending list:\n");
+ i = 0;
+ LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+ if (i++ > AHD_SCB_MAX)
+ break;
+ if (scb != LIST_FIRST(&ahd->pending_scbs))
+ printf(", ");
+ printf("%3d", SCB_GET_TAG(scb));
+ ahd_set_scbptr(ahd, SCB_GET_TAG(scb));
+ printf("(CTRL 0x%x ID 0x%x N 0x%x N2 0x%x SG 0x%x,"
+ " RSG 0x%x, KSG 0x%x)\n",
+ ahd_inb(ahd, SCB_CONTROL),
+ ahd_inb(ahd, SCB_SCSIID), ahd_inw(ahd, SCB_NEXT),
+ ahd_inw(ahd, SCB_NEXT2), ahd_inl(ahd, SCB_SGPTR),
+ ahd_inl(ahd, SCB_RESIDUAL_SGPTR),
+ ahd_le32toh(scb->hscb->sgptr));
+ }
+ printf("\n");
+
+ printf("Kernel Free SCB list: ");
+ i = 0;
+ SLIST_FOREACH(scb, &ahd->scb_data.free_scbs, links.sle) {
+ if (i++ > AHD_SCB_MAX)
+ break;
+ printf("%d ", SCB_GET_TAG(scb));
+ }
+ printf("\n");
+
+ printf("Sequencer Complete DMA-inprog list: ");
+ scb_index = ahd_inw(ahd, COMPLETE_SCB_DMAINPROG_HEAD);
+ i = 0;
+ while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+ ahd_set_scbptr(ahd, scb_index);
+ printf("%d ", scb_index);
+ scb_index = ahd_inw(ahd, SCB_NEXT_COMPLETE);
+ }
+ printf("\n");
+
+ printf("Sequencer Complete list: ");
+ scb_index = ahd_inw(ahd, COMPLETE_SCB_HEAD);
+ i = 0;
+ while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+ ahd_set_scbptr(ahd, scb_index);
+ printf("%d ", scb_index);
+ scb_index = ahd_inw(ahd, SCB_NEXT_COMPLETE);
+ }
+ printf("\n");
+
+
+ printf("Sequencer DMA-Up and Complete list: ");
+ scb_index = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
+ i = 0;
+ while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+ ahd_set_scbptr(ahd, scb_index);
+ printf("%d ", scb_index);
+ scb_index = ahd_inw(ahd, SCB_NEXT_COMPLETE);
+ }
+ printf("\n");
+ printf("%s: SIMODE1 = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, SIMODE1));
+ printf("%s: LQISTAT0 = 0x%x, LQISTAT1 = 0x%x, LQISTAT2 = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, LQISTAT0), ahd_inb(ahd, LQISTAT1),
+ ahd_inb(ahd, LQISTAT2));
+ printf("%s: LQOSTAT0 = 0x%x, LQOSTAT1 = 0x%x, LQOSTAT2 = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, LQOSTAT0), ahd_inb(ahd, LQOSTAT1),
+ ahd_inb(ahd, LQOSTAT2));
+ dffstat = ahd_inb(ahd, DFFSTAT);
+ for (i = 0; i < 2; i++) {
+ struct scb *fifo_scb;
+ u_int fifo_scbptr;
+
+ ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
+ fifo_scbptr = ahd_inb(ahd, SCBPTR);
+ printf("%s: FIFO%d %s, LONGJMP == 0x%x, SCB 0x%x, LJSCB 0x%x\n",
+ ahd_name(ahd), i,
+ (dffstat & (FIFO0FREE << i)) ? "Free" : "Active",
+ ahd_inw(ahd, LONGJMP_ADDR), fifo_scbptr,
+ ahd_inw(ahd, LONGJMP_SCB));
+ printf("%s: SEQIMODE == 0x%x, SEQINTSRC == 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, SEQIMODE),
+ ahd_inb(ahd, SEQINTSRC));
+ printf("%s: DFCNTRL == 0x%x, DFSTATUS == 0x%x, "
+ "SG_CACHE_SHADOW = 0x%x, SG_STATE = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, DFCNTRL),
+ ahd_inb(ahd, DFSTATUS), ahd_inb(ahd, SG_CACHE_SHADOW),
+ ahd_inb(ahd, SG_STATE));
+ printf("SSTAT0 = 0x%x, SSTAT1 = 0x%x, SSTAT2 = 0x%x\n",
+ ahd_inb(ahd, SSTAT0), ahd_inb(ahd, SSTAT1),
+ ahd_inb(ahd, SSTAT2));
+ printf("DFFSXFRCTL = 0x%x, SOFFCNT = 0x%x\n",
+ ahd_inb(ahd, DFFSXFRCTL), ahd_inb(ahd, SOFFCNT));
+ printf("MDFFSTAT = 0x%x, SHADDR = 0x%x, SHCNT = 0x%x\n",
+ ahd_inb(ahd, MDFFSTAT), ahd_inl(ahd, SHADDR),
+ (ahd_inb(ahd, SHCNT)
+ | (ahd_inb(ahd, SHCNT + 1) << 8)
+ | (ahd_inb(ahd, SHCNT + 2) << 16)));
+ printf("HADDR = 0x%x, HCNT = 0x%x\n",
+ ahd_inl(ahd, HADDR),
+ (ahd_inb(ahd, HCNT)
+ | (ahd_inb(ahd, HCNT + 1) << 8)
+ | (ahd_inb(ahd, HCNT + 2) << 16)));
+ printf("CCSGCTL = 0x%x\n", ahd_inb(ahd, CCSGCTL));
+ fifo_scb = ahd_lookup_scb(ahd, fifo_scbptr);
+ if (fifo_scb != NULL)
+ ahd_dump_sglist(fifo_scb);
+ }
+ printf("LQIN: ");
+ for (i = 0; i < 20; i++)
+ printf("0x%x ", ahd_inb(ahd, LQIN + i));
+ printf("\n");
+ printf("%s: SSTAT3 == 0x%x\n", ahd_name(ahd), ahd_inb(ahd, SSTAT3));
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ printf("%s: LQISTATE = 0x%x, LQOSTATE = 0x%x, OPTIONMODE = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, LQISTATE), ahd_inb(ahd, LQOSTATE),
+ ahd_inb(ahd, OPTIONMODE));
+ printf("%s: OS_SPACE_CNT = 0x%x MAXCMDCNT = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, OS_SPACE_CNT),
+ ahd_inb(ahd, MAXCMDCNT));
+ ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+ printf("%s: REG0 == 0x%x, SINDEX = 0x%x, DINDEX = 0x%x\n",
+ ahd_name(ahd), ahd_inw(ahd, REG0), ahd_inw(ahd, SINDEX),
+ ahd_inw(ahd, DINDEX));
+ printf("%s: SCBPTR == 0x%x, SCB_NEXT == 0x%x, SCB_NEXT2 == 0x%x\n",
+ ahd_name(ahd), ahd_get_scbptr(ahd), ahd_inw(ahd, SCB_NEXT),
+ ahd_inw(ahd, SCB_NEXT2));
+ printf("CDB %x %x %x %x %x %x\n",
+ ahd_inb(ahd, SCB_CDB_STORE),
+ ahd_inb(ahd, SCB_CDB_STORE+1),
+ ahd_inb(ahd, SCB_CDB_STORE+2),
+ ahd_inb(ahd, SCB_CDB_STORE+3),
+ ahd_inb(ahd, SCB_CDB_STORE+4),
+ ahd_inb(ahd, SCB_CDB_STORE+5));
+ printf("STACK:");
+ for(i = 0; i < SEQ_STACK_SIZE; i++)
+ printf(" 0x%x", ahd_inb(ahd, STACK)|(ahd_inb(ahd, STACK) << 8));
+ printf("\n");
+ ahd_platform_dump_card_state(ahd);
+ ahd_restore_modes(ahd, saved_modes);
+ if (paused == 0)
+ ahd_unpause(ahd);
+}
+
+void
+ahd_dump_scbs(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ int i;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ for (i = 0; i < AHD_SCB_MAX; i++) {
+ ahd_set_scbptr(ahd, i);
+ printf("%3d", i);
+ printf("(CTRL 0x%x ID 0x%x N 0x%x N2 0x%x SG 0x%x, RSG 0x%x)\n",
+ ahd_inb(ahd, SCB_CONTROL),
+ ahd_inb(ahd, SCB_SCSIID), ahd_inw(ahd, SCB_NEXT),
+ ahd_inw(ahd, SCB_NEXT2), ahd_inl(ahd, SCB_SGPTR),
+ ahd_inl(ahd, SCB_RESIDUAL_SGPTR));
+ }
+ printf("\n");
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/**************************** Flexport Logic **********************************/
+/*
+ * Read count 16bit words from 16bit word address start_addr from the
+ * SEEPROM attached to the controller, into buf, using the controller's
+ * SEEPROM reading state machine.
+ */
+int
+ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+ u_int start_addr, u_int count)
+{
+ u_int cur_addr;
+ u_int end_addr;
+ int error;
+
+ /*
+ * If we never make it through the loop even once,
+ * we were passed invalid arguments.
+ */
+ error = EINVAL;
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ end_addr = start_addr + count;
+ for (cur_addr = start_addr; cur_addr < end_addr; cur_addr++) {
+ ahd_outb(ahd, SEEADR, cur_addr);
+ ahd_outb(ahd, SEECTL, SEEOP_READ | SEESTART);
+
+ error = ahd_wait_seeprom(ahd);
+ if (error)
+ break;
+ *buf++ = ahd_inw(ahd, SEEDAT);
+ }
+ return (error);
+}
+
+/*
+ * Write count 16bit words from buf, into SEEPROM attache to the
+ * controller starting at 16bit word address start_addr, using the
+ * controller's SEEPROM writing state machine.
+ */
+int
+ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+ u_int start_addr, u_int count)
+{
+ u_int cur_addr;
+ u_int end_addr;
+ int error;
+ int retval;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ error = ENOENT;
+
+ /* Place the chip into write-enable mode */
+ ahd_outb(ahd, SEEADR, SEEOP_EWEN_ADDR);
+ ahd_outb(ahd, SEECTL, SEEOP_EWEN | SEESTART);
+ error = ahd_wait_seeprom(ahd);
+ if (error)
+ return (error);
+
+ /*
+ * Write the data. If we don't get throught the loop at
+ * least once, the arguments were invalid.
+ */
+ retval = EINVAL;
+ end_addr = start_addr + count;
+ for (cur_addr = start_addr; cur_addr < end_addr; cur_addr++) {
+ ahd_outw(ahd, SEEDAT, *buf++);
+ ahd_outb(ahd, SEEADR, cur_addr);
+ ahd_outb(ahd, SEECTL, SEEOP_WRITE | SEESTART);
+
+ retval = ahd_wait_seeprom(ahd);
+ if (retval)
+ break;
+ }
+
+ /*
+ * Disable writes.
+ */
+ ahd_outb(ahd, SEEADR, SEEOP_EWDS_ADDR);
+ ahd_outb(ahd, SEECTL, SEEOP_EWDS | SEESTART);
+ error = ahd_wait_seeprom(ahd);
+ if (error)
+ return (error);
+ return (retval);
+}
+
+/*
+ * Wait ~100us for the serial eeprom to satisfy our request.
+ */
+int
+ahd_wait_seeprom(struct ahd_softc *ahd)
+{
+ int cnt;
+
+ cnt = 20;
+ while ((ahd_inb(ahd, SEESTAT) & (SEEARBACK|SEEBUSY)) != 0 && --cnt)
+ ahd_delay(5);
+
+ if (cnt == 0)
+ return (ETIMEDOUT);
+ return (0);
+}
+
+int
+ahd_verify_cksum(struct seeprom_config *sc)
+{
+ int i;
+ int maxaddr;
+ uint32_t checksum;
+ uint16_t *scarray;
+
+ maxaddr = (sizeof(*sc)/2) - 1;
+ checksum = 0;
+ scarray = (uint16_t *)sc;
+
+ for (i = 0; i < maxaddr; i++)
+ checksum = checksum + scarray[i];
+ if (checksum == 0
+ || (checksum & 0xFFFF) != sc->checksum) {
+ return (0);
+ } else {
+ return (1);
+ }
+}
+
+int
+ahd_acquire_seeprom(struct ahd_softc *ahd)
+{
+ uint8_t seetype;
+ int error;
+
+ error = ahd_read_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, &seetype);
+ if (error != 0
+ || ((seetype & FLX_ROMSTAT_SEECFG) == FLX_ROMSTAT_SEE_NONE))
+ return (0);
+ return (1);
+}
+
+void
+ahd_release_seeprom(struct ahd_softc *ahd)
+{
+ /* Currently a no-op */
+}
+
+int
+ahd_write_flexport(struct ahd_softc *ahd, u_int addr, u_int value)
+{
+ int error;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ if (addr > 7)
+ panic("ahd_write_flexport: address out of range");
+ ahd_outb(ahd, BRDCTL, BRDEN|(addr << 3));
+ error = ahd_wait_flexport(ahd);
+ if (error != 0)
+ return (error);
+ ahd_outb(ahd, BRDDAT, value);
+ ahd_flush_device_writes(ahd);
+ ahd_outb(ahd, BRDCTL, BRDSTB|BRDEN|(addr << 3));
+ ahd_flush_device_writes(ahd);
+ ahd_outb(ahd, BRDCTL, BRDEN|(addr << 3));
+ ahd_flush_device_writes(ahd);
+ ahd_outb(ahd, BRDCTL, 0);
+ ahd_flush_device_writes(ahd);
+ return (0);
+}
+
+int
+ahd_read_flexport(struct ahd_softc *ahd, u_int addr, uint8_t *value)
+{
+ int error;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ if (addr > 7)
+ panic("ahd_read_flexport: address out of range");
+ ahd_outb(ahd, BRDCTL, BRDRW|BRDEN|(addr << 3));
+ error = ahd_wait_flexport(ahd);
+ if (error != 0)
+ return (error);
+ *value = ahd_inb(ahd, BRDDAT);
+ ahd_outb(ahd, BRDCTL, 0);
+ ahd_flush_device_writes(ahd);
+ return (0);
+}
+
+/*
+ * Wait at most 2 seconds for flexport arbitration to succeed.
+ */
+int
+ahd_wait_flexport(struct ahd_softc *ahd)
+{
+ int cnt;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ cnt = 1000000 * 2 / 5;
+ while ((ahd_inb(ahd, BRDCTL) & FLXARBACK) == 0 && --cnt)
+ ahd_delay(5);
+
+ if (cnt == 0)
+ return (ETIMEDOUT);
+ return (0);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHD_TARGET_MODE
+cam_status
+ahd_find_tmode_devs(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb,
+ struct ahd_tmode_tstate **tstate,
+ struct ahd_tmode_lstate **lstate,
+ int notfound_failure)
+{
+
+ if ((ahd->features & AHD_TARGETMODE) == 0)
+ return (CAM_REQ_INVALID);
+
+ /*
+ * Handle the 'black hole' device that sucks up
+ * requests to unattached luns on enabled targets.
+ */
+ if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+ && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+ *tstate = NULL;
+ *lstate = ahd->black_hole;
+ } else {
+ u_int max_id;
+
+ max_id = (ahd->features & AHD_WIDE) ? 15 : 7;
+ if (ccb->ccb_h.target_id > max_id)
+ return (CAM_TID_INVALID);
+
+ if (ccb->ccb_h.target_lun >= AHD_NUM_LUNS)
+ return (CAM_LUN_INVALID);
+
+ *tstate = ahd->enabled_targets[ccb->ccb_h.target_id];
+ *lstate = NULL;
+ if (*tstate != NULL)
+ *lstate =
+ (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+ }
+
+ if (notfound_failure != 0 && *lstate == NULL)
+ return (CAM_PATH_INVALID);
+
+ return (CAM_REQ_CMP);
+}
+
+void
+ahd_handle_en_lun(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb)
+{
+#if NOT_YET
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_tmode_lstate *lstate;
+ struct ccb_en_lun *cel;
+ cam_status status;
+ u_int target;
+ u_int lun;
+ u_int target_mask;
+ u_long s;
+ char channel;
+
+ status = ahd_find_tmode_devs(ahd, sim, ccb, &tstate, &lstate,
+ /*notfound_failure*/FALSE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ if ((ahd->features & AHD_MULTIROLE) != 0) {
+ u_int our_id;
+
+ our_id = ahd->our_id;
+ if (ccb->ccb_h.target_id != our_id) {
+ if ((ahd->features & AHD_MULTI_TID) != 0
+ && (ahd->flags & AHD_INITIATORROLE) != 0) {
+ /*
+ * Only allow additional targets if
+ * the initiator role is disabled.
+ * The hardware cannot handle a re-select-in
+ * on the initiator id during a re-select-out
+ * on a different target id.
+ */
+ status = CAM_TID_INVALID;
+ } else if ((ahd->flags & AHD_INITIATORROLE) != 0
+ || ahd->enabled_luns > 0) {
+ /*
+ * Only allow our target id to change
+ * if the initiator role is not configured
+ * and there are no enabled luns which
+ * are attached to the currently registered
+ * scsi id.
+ */
+ status = CAM_TID_INVALID;
+ }
+ }
+ }
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ /*
+ * We now have an id that is valid.
+ * If we aren't in target mode, switch modes.
+ */
+ if ((ahd->flags & AHD_TARGETROLE) == 0
+ && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+ u_long s;
+
+ printf("Configuring Target Mode\n");
+ ahd_lock(ahd, &s);
+ if (LIST_FIRST(&ahd->pending_scbs) != NULL) {
+ ccb->ccb_h.status = CAM_BUSY;
+ ahd_unlock(ahd, &s);
+ return;
+ }
+ ahd->flags |= AHD_TARGETROLE;
+ if ((ahd->features & AHD_MULTIROLE) == 0)
+ ahd->flags &= ~AHD_INITIATORROLE;
+ ahd_pause(ahd);
+ ahd_loadseq(ahd);
+ ahd_unlock(ahd, &s);
+ }
+ cel = &ccb->cel;
+ target = ccb->ccb_h.target_id;
+ lun = ccb->ccb_h.target_lun;
+ channel = SIM_CHANNEL(ahd, sim);
+ target_mask = 0x01 << target;
+ if (channel == 'B')
+ target_mask <<= 8;
+
+ if (cel->enable != 0) {
+ u_int scsiseq1;
+
+ /* Are we already enabled?? */
+ if (lstate != NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun already enabled\n");
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ return;
+ }
+
+ if (cel->grp6_len != 0
+ || cel->grp7_len != 0) {
+ /*
+ * Don't (yet?) support vendor
+ * specific commands.
+ */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ printf("Non-zero Group Codes\n");
+ return;
+ }
+
+ /*
+ * Seems to be okay.
+ * Setup our data structures.
+ */
+ if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+ tstate = ahd_alloc_tstate(ahd, target, channel);
+ if (tstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate tstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ }
+ lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+ if (lstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate lstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ memset(lstate, 0, sizeof(*lstate));
+ status = xpt_create_path(&lstate->path, /*periph*/NULL,
+ xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ xpt_path_lun_id(ccb->ccb_h.path));
+ if (status != CAM_REQ_CMP) {
+ free(lstate, M_DEVBUF);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate path\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ SLIST_INIT(&lstate->accept_tios);
+ SLIST_INIT(&lstate->immed_notifies);
+ ahd_lock(ahd, &s);
+ ahd_pause(ahd);
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = lstate;
+ ahd->enabled_luns++;
+
+ if ((ahd->features & AHD_MULTI_TID) != 0) {
+ u_int targid_mask;
+
+ targid_mask = ahd_inb(ahd, TARGID)
+ | (ahd_inb(ahd, TARGID + 1) << 8);
+
+ targid_mask |= target_mask;
+ ahd_outb(ahd, TARGID, targid_mask);
+ ahd_outb(ahd, TARGID+1, (targid_mask >> 8));
+
+ ahd_update_scsiid(ahd, targid_mask);
+ } else {
+ u_int our_id;
+ char channel;
+
+ channel = SIM_CHANNEL(ahd, sim);
+ our_id = SIM_SCSI_ID(ahd, sim);
+
+ /*
+ * This can only happen if selections
+ * are not enabled
+ */
+ if (target != our_id) {
+ u_int sblkctl;
+ char cur_channel;
+ int swap;
+
+ sblkctl = ahd_inb(ahd, SBLKCTL);
+ cur_channel = (sblkctl & SELBUSB)
+ ? 'B' : 'A';
+ if ((ahd->features & AHD_TWIN) == 0)
+ cur_channel = 'A';
+ swap = cur_channel != channel;
+ ahd->our_id = target;
+
+ if (swap)
+ ahd_outb(ahd, SBLKCTL,
+ sblkctl ^ SELBUSB);
+
+ ahd_outb(ahd, SCSIID, target);
+
+ if (swap)
+ ahd_outb(ahd, SBLKCTL, sblkctl);
+ }
+ }
+ } else
+ ahd->black_hole = lstate;
+ /* Allow select-in operations */
+ if (ahd->black_hole != NULL && ahd->enabled_luns > 0) {
+ scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+ scsiseq1 |= ENSELI;
+ ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq1);
+ scsiseq1 = ahd_inb(ahd, SCSISEQ1);
+ scsiseq1 |= ENSELI;
+ ahd_outb(ahd, SCSISEQ1, scsiseq1);
+ }
+ ahd_unpause(ahd);
+ ahd_unlock(ahd, &s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun now enabled for target mode\n");
+ } else {
+ struct scb *scb;
+ int i, empty;
+
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+
+ ahd_lock(ahd, &s);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+ struct ccb_hdr *ccbh;
+
+ ccbh = &scb->io_ctx->ccb_h;
+ if (ccbh->func_code == XPT_CONT_TARGET_IO
+ && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+ printf("CTIO pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ ahd_unlock(ahd, &s);
+ return;
+ }
+ }
+
+ if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+ printf("ATIOs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+ printf("INOTs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ ahd_unlock(ahd, &s);
+ return;
+ }
+
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Target mode disabled\n");
+ xpt_free_path(lstate->path);
+ free(lstate, M_DEVBUF);
+
+ ahd_pause(ahd);
+ /* Can we clean up the target too? */
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = NULL;
+ ahd->enabled_luns--;
+ for (empty = 1, i = 0; i < 8; i++)
+ if (tstate->enabled_luns[i] != NULL) {
+ empty = 0;
+ break;
+ }
+
+ if (empty) {
+ ahd_free_tstate(ahd, target, channel,
+ /*force*/FALSE);
+ if (ahd->features & AHD_MULTI_TID) {
+ u_int targid_mask;
+
+ targid_mask = ahd_inb(ahd, TARGID)
+ | (ahd_inb(ahd, TARGID + 1)
+ << 8);
+
+ targid_mask &= ~target_mask;
+ ahd_outb(ahd, TARGID, targid_mask);
+ ahd_outb(ahd, TARGID+1,
+ (targid_mask >> 8));
+ ahd_update_scsiid(ahd, targid_mask);
+ }
+ }
+ } else {
+
+ ahd->black_hole = NULL;
+
+ /*
+ * We can't allow selections without
+ * our black hole device.
+ */
+ empty = TRUE;
+ }
+ if (ahd->enabled_luns == 0) {
+ /* Disallow select-in */
+ u_int scsiseq1;
+
+ scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+ scsiseq1 &= ~ENSELI;
+ ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq1);
+ scsiseq1 = ahd_inb(ahd, SCSISEQ1);
+ scsiseq1 &= ~ENSELI;
+ ahd_outb(ahd, SCSISEQ1, scsiseq1);
+
+ if ((ahd->features & AHD_MULTIROLE) == 0) {
+ printf("Configuring Initiator Mode\n");
+ ahd->flags &= ~AHD_TARGETROLE;
+ ahd->flags |= AHD_INITIATORROLE;
+ ahd_pause(ahd);
+ ahd_loadseq(ahd);
+ }
+ }
+ ahd_unpause(ahd);
+ ahd_unlock(ahd, &s);
+ }
+#endif
+}
+
+static void
+ahd_update_scsiid(struct ahd_softc *ahd, u_int targid_mask)
+{
+#if NOT_YET
+ u_int scsiid_mask;
+ u_int scsiid;
+
+ if ((ahd->features & AHD_MULTI_TID) == 0)
+ panic("ahd_update_scsiid called on non-multitid unit\n");
+
+ /*
+ * Since we will rely on the the TARGID mask
+ * for selection enables, ensure that OID
+ * in SCSIID is not set to some other ID
+ * that we don't want to allow selections on.
+ */
+ if ((ahd->features & AHD_ULTRA2) != 0)
+ scsiid = ahd_inb(ahd, SCSIID_ULTRA2);
+ else
+ scsiid = ahd_inb(ahd, SCSIID);
+ scsiid_mask = 0x1 << (scsiid & OID);
+ if ((targid_mask & scsiid_mask) == 0) {
+ u_int our_id;
+
+ /* ffs counts from 1 */
+ our_id = ffs(targid_mask);
+ if (our_id == 0)
+ our_id = ahd->our_id;
+ else
+ our_id--;
+ scsiid &= TID;
+ scsiid |= our_id;
+ }
+ if ((ahd->features & AHD_ULTRA2) != 0)
+ ahd_outb(ahd, SCSIID_ULTRA2, scsiid);
+ else
+ ahd_outb(ahd, SCSIID, scsiid);
+#endif
+}
+
+void
+ahd_run_tqinfifo(struct ahd_softc *ahd, int paused)
+{
+ struct target_cmd *cmd;
+
+ ahd_sync_tqinfifo(ahd, BUS_DMASYNC_POSTREAD);
+ while ((cmd = &ahd->targetcmds[ahd->tqinfifonext])->cmd_valid != 0) {
+
+ /*
+ * Only advance through the queue if we
+ * have the resources to process the command.
+ */
+ if (ahd_handle_target_cmd(ahd, cmd) != 0)
+ break;
+
+ cmd->cmd_valid = 0;
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap,
+ ahd_targetcmd_offset(ahd, ahd->tqinfifonext),
+ sizeof(struct target_cmd),
+ BUS_DMASYNC_PREREAD);
+ ahd->tqinfifonext++;
+
+ /*
+ * Lazily update our position in the target mode incoming
+ * command queue as seen by the sequencer.
+ */
+ if ((ahd->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+ u_int hs_mailbox;
+
+ hs_mailbox = ahd_inb(ahd, HS_MAILBOX);
+ hs_mailbox &= ~HOST_TQINPOS;
+ hs_mailbox |= ahd->tqinfifonext & HOST_TQINPOS;
+ ahd_outb(ahd, HS_MAILBOX, hs_mailbox);
+ }
+ }
+}
+
+static int
+ahd_handle_target_cmd(struct ahd_softc *ahd, struct target_cmd *cmd)
+{
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_tmode_lstate *lstate;
+ struct ccb_accept_tio *atio;
+ uint8_t *byte;
+ int initiator;
+ int target;
+ int lun;
+
+ initiator = SCSIID_TARGET(ahd, cmd->scsiid);
+ target = SCSIID_OUR_ID(cmd->scsiid);
+ lun = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+ byte = cmd->bytes;
+ tstate = ahd->enabled_targets[target];
+ lstate = NULL;
+ if (tstate != NULL)
+ lstate = tstate->enabled_luns[lun];
+
+ /*
+ * Commands for disabled luns go to the black hole driver.
+ */
+ if (lstate == NULL)
+ lstate = ahd->black_hole;
+
+ atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+ if (atio == NULL) {
+ ahd->flags |= AHD_TQINFIFO_BLOCKED;
+ /*
+ * Wait for more ATIOs from the peripheral driver for this lun.
+ */
+ return (1);
+ } else
+ ahd->flags &= ~AHD_TQINFIFO_BLOCKED;
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_TQIN) != 0)
+ printf("Incoming command from %d for %d:%d%s\n",
+ initiator, target, lun,
+ lstate == ahd->black_hole ? "(Black Holed)" : "");
+#endif
+ SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+ if (lstate == ahd->black_hole) {
+ /* Fill in the wildcards */
+ atio->ccb_h.target_id = target;
+ atio->ccb_h.target_lun = lun;
+ }
+
+ /*
+ * Package it up and send it off to
+ * whomever has this lun enabled.
+ */
+ atio->sense_len = 0;
+ atio->init_id = initiator;
+ if (byte[0] != 0xFF) {
+ /* Tag was included */
+ atio->tag_action = *byte++;
+ atio->tag_id = *byte++;
+ atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+ } else {
+ atio->ccb_h.flags = 0;
+ }
+ byte++;
+
+ /* Okay. Now determine the cdb size based on the command code */
+ switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+ case 0:
+ atio->cdb_len = 6;
+ break;
+ case 1:
+ case 2:
+ atio->cdb_len = 10;
+ break;
+ case 4:
+ atio->cdb_len = 16;
+ break;
+ case 5:
+ atio->cdb_len = 12;
+ break;
+ case 3:
+ default:
+ /* Only copy the opcode. */
+ atio->cdb_len = 1;
+ printf("Reserved or VU command code type encountered\n");
+ break;
+ }
+
+ memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+ atio->ccb_h.status |= CAM_CDB_RECVD;
+
+ if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+ /*
+ * We weren't allowed to disconnect.
+ * We're hanging on the bus until a
+ * continue target I/O comes in response
+ * to this accept tio.
+ */
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_TQIN) != 0)
+ printf("Received Immediate Command %d:%d:%d - %p\n",
+ initiator, target, lun, ahd->pending_device);
+#endif
+ ahd->pending_device = lstate;
+ ahd_freeze_ccb((union ccb *)atio);
+ atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+ }
+ xpt_done((union ccb*)atio);
+ return (0);
+}
+
+#endif
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_host.h b/xen/drivers/scsi/aic7xxx/aic79xx_host.h
new file mode 100644
index 0000000000..5427eac459
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_host.h
@@ -0,0 +1,95 @@
+/*
+ * Adaptec AIC79xx device driver host template for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_host.h#6 $
+ */
+
+#ifndef _AIC79XX_LINUX_HOST_H_
+#define _AIC79XX_LINUX_HOST_H_
+
+int ahd_linux_proc_info(char *, char **, off_t, int, int, int);
+int ahd_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+int ahd_linux_detect(Scsi_Host_Template *);
+int ahd_linux_release(struct Scsi_Host *);
+const char *ahd_linux_info(struct Scsi_Host *);
+int ahd_linux_biosparam(Disk *, kdev_t, int[]);
+int ahd_linux_bus_reset(Scsi_Cmnd *);
+int ahd_linux_dev_reset(Scsi_Cmnd *);
+int ahd_linux_abort(Scsi_Cmnd *);
+
+#if defined(__i386__)
+# define AIC79XX_BIOSPARAM ahd_linux_biosparam
+#else
+# define AIC79XX_BIOSPARAM NULL
+#endif
+
+/*
+ * Scsi_Host_Template (see hosts.h) for AIC-79xx - some fields
+ * to do with card config are filled in after the card is detected.
+ */
+#define AIC79XX { \
+ next: NULL, \
+ module: NULL, \
+ proc_dir: NULL, \
+ proc_info: ahd_linux_proc_info, \
+ name: NULL, \
+ detect: ahd_linux_detect, \
+ release: ahd_linux_release, \
+ info: ahd_linux_info, \
+ command: NULL, \
+ queuecommand: ahd_linux_queue, \
+ eh_strategy_handler: NULL, \
+ eh_abort_handler: ahd_linux_abort, \
+ eh_device_reset_handler: ahd_linux_dev_reset, \
+ eh_bus_reset_handler: ahd_linux_bus_reset, \
+ eh_host_reset_handler: NULL, \
+ abort: NULL, \
+ reset: NULL, \
+ slave_attach: NULL, \
+ bios_param: AIC79XX_BIOSPARAM, \
+ can_queue: AHD_MAX_QUEUE,/* max simultaneous cmds */\
+ this_id: -1, /* scsi id of host adapter */\
+ sg_tablesize: AHD_NSEG, /* max scatter-gather cmds */\
+ cmd_per_lun: 2, /* cmds per lun */\
+ present: 0, /* number of 7xxx's present */\
+ unchecked_isa_dma: 0, /* no memory DMA restrictions*/\
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 1 \
+}
+
+#endif /* _AIC79XX_LINUX_HOST_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_inline.h b/xen/drivers/scsi/aic7xxx/aic79xx_inline.h
new file mode 100644
index 0000000000..72750fc18a
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_inline.h
@@ -0,0 +1,997 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx_inline.h#28 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC79XX_INLINE_H_
+#define _AIC79XX_INLINE_H_
+
+/******************************** Debugging ***********************************/
+static __inline char *ahd_name(struct ahd_softc *ahd);
+
+static __inline char *
+ahd_name(struct ahd_softc *ahd)
+{
+ return (ahd->name);
+}
+
+/************************ Sequencer Execution Control *************************/
+static __inline void ahd_known_modes(struct ahd_softc *ahd,
+ ahd_mode src, ahd_mode dst);
+static __inline ahd_mode_state ahd_build_mode_state(struct ahd_softc *ahd,
+ ahd_mode src,
+ ahd_mode dst);
+static __inline void ahd_extract_mode_state(struct ahd_softc *ahd,
+ ahd_mode_state state,
+ ahd_mode *src, ahd_mode *dst);
+static __inline void ahd_set_modes(struct ahd_softc *ahd, ahd_mode src,
+ ahd_mode dst);
+static __inline void ahd_update_modes(struct ahd_softc *ahd);
+static __inline void ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode,
+ ahd_mode dstmode, const char *file,
+ int line);
+static __inline ahd_mode_state ahd_save_modes(struct ahd_softc *ahd);
+static __inline void ahd_restore_modes(struct ahd_softc *ahd,
+ ahd_mode_state state);
+static __inline int ahd_is_paused(struct ahd_softc *ahd);
+static __inline void ahd_pause(struct ahd_softc *ahd);
+static __inline void ahd_unpause(struct ahd_softc *ahd);
+
+static __inline void
+ahd_known_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+ ahd->src_mode = src;
+ ahd->dst_mode = dst;
+ ahd->saved_src_mode = src;
+ ahd->saved_dst_mode = dst;
+}
+
+static __inline ahd_mode_state
+ahd_build_mode_state(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+ return ((src << SRC_MODE_SHIFT) | (dst << DST_MODE_SHIFT));
+}
+
+static __inline void
+ahd_extract_mode_state(struct ahd_softc *ahd, ahd_mode_state state,
+ ahd_mode *src, ahd_mode *dst)
+{
+ *src = (state & SRC_MODE) >> SRC_MODE_SHIFT;
+ *dst = (state & DST_MODE) >> DST_MODE_SHIFT;
+}
+
+static __inline void
+ahd_set_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+ if (ahd->src_mode == src && ahd->dst_mode == dst)
+ return;
+#ifdef AHD_DEBUG
+ if (ahd->src_mode == AHD_MODE_UNKNOWN
+ || ahd->dst_mode == AHD_MODE_UNKNOWN)
+ panic("Setting mode prior to saving it.\n");
+ if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
+ printf("Setting mode 0x%x\n",
+ ahd_build_mode_state(ahd, src, dst));
+#endif
+ ahd_outb(ahd, MODE_PTR, ahd_build_mode_state(ahd, src, dst));
+ ahd->src_mode = src;
+ ahd->dst_mode = dst;
+}
+
+static __inline void
+ahd_update_modes(struct ahd_softc *ahd)
+{
+ ahd_mode_state mode_ptr;
+ ahd_mode src;
+ ahd_mode dst;
+
+ mode_ptr = ahd_inb(ahd, MODE_PTR);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
+ printf("Reading mode 0x%x\n", mode_ptr);
+#endif
+ ahd_extract_mode_state(ahd, mode_ptr, &src, &dst);
+ ahd_known_modes(ahd, src, dst);
+}
+
+static __inline void
+ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode,
+ ahd_mode dstmode, const char *file, int line)
+{
+#ifdef AHD_DEBUG
+ if ((srcmode & AHD_MK_MSK(ahd->src_mode)) == 0
+ || (dstmode & AHD_MK_MSK(ahd->dst_mode)) == 0) {
+ panic("%s:%s:%d: Mode assertion failed.\n",
+ ahd_name(ahd), file, line);
+ }
+#endif
+}
+
+static __inline ahd_mode_state
+ahd_save_modes(struct ahd_softc *ahd)
+{
+ if (ahd->src_mode == AHD_MODE_UNKNOWN
+ || ahd->dst_mode == AHD_MODE_UNKNOWN)
+ ahd_update_modes(ahd);
+
+ return (ahd_build_mode_state(ahd, ahd->src_mode, ahd->dst_mode));
+}
+
+static __inline void
+ahd_restore_modes(struct ahd_softc *ahd, ahd_mode_state state)
+{
+ ahd_mode src;
+ ahd_mode dst;
+
+ ahd_extract_mode_state(ahd, state, &src, &dst);
+ ahd_set_modes(ahd, src, dst);
+}
+
+#define AHD_ASSERT_MODES(ahd, source, dest) \
+ ahd_assert_modes(ahd, source, dest, __FILE__, __LINE__);
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+static __inline int
+ahd_is_paused(struct ahd_softc *ahd)
+{
+ return ((ahd_inb(ahd, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop. The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register. The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+static __inline void
+ahd_pause(struct ahd_softc *ahd)
+{
+ ahd_outb(ahd, HCNTRL, ahd->pause);
+
+ /*
+ * Since the sequencer can disable pausing in a critical section, we
+ * must loop until it actually stops.
+ */
+ while (ahd_is_paused(ahd) == 0)
+ ;
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted. If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+static __inline void
+ahd_unpause(struct ahd_softc *ahd)
+{
+ /*
+ * Automatically restore our modes to those saved
+ * prior to the first change of the mode.
+ */
+ if (ahd->saved_src_mode != AHD_MODE_UNKNOWN
+ && ahd->saved_dst_mode != AHD_MODE_UNKNOWN)
+ ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+
+ if ((ahd_inb(ahd, INTSTAT) & ~(SWTMINT | CMDCMPLT)) == 0)
+ ahd_outb(ahd, HCNTRL, ahd->unpause);
+
+ ahd_known_modes(ahd, AHD_MODE_UNKNOWN, AHD_MODE_UNKNOWN);
+}
+
+/*********************** Untagged Transaction Routines ************************/
+static __inline void ahd_freeze_untagged_queues(struct ahd_softc *ahd);
+static __inline void ahd_release_untagged_queues(struct ahd_softc *ahd);
+
+/*
+ * Block our completion routine from starting the next untagged
+ * transaction for this target or target lun.
+ */
+static __inline void
+ahd_freeze_untagged_queues(struct ahd_softc *ahd)
+{
+ /*
+ * Assume we have enough space in the card's SCB
+ * to obviate the need for a per target untagged
+ * transaction limit.
+ */
+#if 0
+ ahd->untagged_queue_lock++;
+#endif
+}
+
+/*
+ * Allow the next untagged transaction for this target or target lun
+ * to be executed. We use a counting semaphore to allow the lock
+ * to be acquired recursively. Once the count drops to zero, the
+ * transaction queues will be run.
+ */
+static __inline void
+ahd_release_untagged_queues(struct ahd_softc *ahd)
+{
+ /*
+ * Assume we have enough space in the card's SCB
+ * to obviate the need for a per target untagged
+ * transaction limit.
+ */
+#if 0
+ ahd->untagged_queue_lock--;
+ if (ahd->untagged_queue_lock == 0)
+ ahd_run_untagged_queues(ahd);
+#endif
+}
+
+/*********************** Scatter Gather List Handling *************************/
+static __inline void *ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb,
+ void *sgptr, bus_addr_t addr,
+ bus_size_t len, int last);
+static __inline void ahd_setup_scb_common(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_setup_data_scb(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_setup_noxfer_scb(struct ahd_softc *ahd,
+ struct scb *scb);
+
+static __inline void *
+ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb,
+ void *sgptr, bus_addr_t addr, bus_size_t len, int last)
+{
+ scb->sg_count++;
+ if (sizeof(bus_addr_t) > 4
+ && (ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg;
+
+ sg = (struct ahd_dma64_seg *)sgptr;
+ sg->addr = ahd_htole64(addr);
+ sg->len = ahd_htole32(len | (last ? AHD_DMA_LAST_SEG : 0));
+ return (sg + 1);
+ } else {
+ struct ahd_dma_seg *sg;
+
+ sg = (struct ahd_dma_seg *)sgptr;
+ sg->addr = ahd_htole64(addr);
+ sg->len = ahd_htole32(len | ((addr >> 8) & 0x7F000000)
+ | (last ? AHD_DMA_LAST_SEG : 0));
+ return (sg + 1);
+ }
+}
+
+static __inline void
+ahd_setup_scb_common(struct ahd_softc *ahd, struct scb *scb)
+{
+ /* XXX Handle target mode SCBs. */
+ if ((scb->flags & SCB_PACKETIZED) != 0) {
+ /* XXX what about ACA?? It is type 4, but TAG_TYPE == 0x3. */
+ scb->hscb->task_attribute_nonpkt_tag =
+ scb->hscb->control & SCB_TAG_TYPE;
+ scb->hscb->task_management = 0;
+ } else {
+ scb->hscb->task_attribute_nonpkt_tag = SCB_GET_TAG(scb);
+ }
+
+ if (scb->hscb->cdb_len <= MAX_CDB_LEN_WITH_SENSE_ADDR
+ || (scb->hscb->cdb_len & SCB_CDB_LEN_PTR) != 0)
+ scb->hscb->shared_data.idata.cdb_plus_saddr.sense_addr =
+ ahd_htole32(scb->sense_busaddr);
+}
+
+static __inline void
+ahd_setup_data_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ /*
+ * Copy the first SG into the "current" data ponter area.
+ */
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg;
+
+ sg = (struct ahd_dma64_seg *)scb->sg_list;
+ scb->hscb->dataptr = sg->addr;
+ scb->hscb->datacnt = sg->len;
+ } else {
+ struct ahd_dma_seg *sg;
+
+ sg = (struct ahd_dma_seg *)scb->sg_list;
+ scb->hscb->dataptr = sg->addr;
+ if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+ uint64_t high_addr;
+
+ high_addr = ahd_le32toh(sg->len) & 0x7F000000;
+ scb->hscb->dataptr |= ahd_htole64(high_addr << 8);
+ }
+ scb->hscb->datacnt = sg->len;
+ }
+ /*
+ * Note where to find the SG entries in bus space.
+ * We also set the full residual flag which the
+ * sequencer will clear as soon as a data transfer
+ * occurs.
+ */
+ scb->hscb->sgptr = ahd_htole32(scb->sg_list_busaddr|SG_FULL_RESID);
+}
+
+static __inline void
+ahd_setup_noxfer_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ scb->hscb->sgptr = ahd_htole32(SG_LIST_NULL);
+ scb->hscb->dataptr = 0;
+ scb->hscb->datacnt = 0;
+}
+
+/************************** Memory mapping routines ***************************/
+static __inline size_t ahd_sg_size(struct ahd_softc *ahd);
+static __inline void *
+ ahd_sg_bus_to_virt(struct ahd_softc *ahd,
+ struct scb *scb,
+ uint32_t sg_busaddr);
+static __inline uint32_t
+ ahd_sg_virt_to_bus(struct ahd_softc *ahd,
+ struct scb *scb,
+ void *sg);
+static __inline void ahd_sync_scb(struct ahd_softc *ahd,
+ struct scb *scb, int op);
+static __inline void ahd_sync_sglist(struct ahd_softc *ahd,
+ struct scb *scb, int op);
+static __inline void ahd_sync_sense(struct ahd_softc *ahd,
+ struct scb *scb, int op);
+static __inline uint32_t
+ ahd_targetcmd_offset(struct ahd_softc *ahd,
+ u_int index);
+
+static __inline size_t
+ahd_sg_size(struct ahd_softc *ahd)
+{
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+ return (sizeof(struct ahd_dma64_seg));
+ return (sizeof(struct ahd_dma_seg));
+}
+
+static __inline void *
+ahd_sg_bus_to_virt(struct ahd_softc *ahd, struct scb *scb, uint32_t sg_busaddr)
+{
+ bus_addr_t sg_offset;
+
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_offset = sg_busaddr - (scb->sg_list_busaddr - ahd_sg_size(ahd));
+ return ((uint8_t *)scb->sg_list + sg_offset);
+}
+
+static __inline uint32_t
+ahd_sg_virt_to_bus(struct ahd_softc *ahd, struct scb *scb, void *sg)
+{
+ bus_addr_t sg_offset;
+
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_offset = ((uint8_t *)sg - (uint8_t *)scb->sg_list)
+ - ahd_sg_size(ahd);
+
+ return (scb->sg_list_busaddr + sg_offset);
+}
+
+static __inline void
+ahd_sync_scb(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+ ahd_dmamap_sync(ahd, ahd->scb_data.hscb_dmat,
+ scb->hscb_map->dmamap,
+ /*offset*/(uint8_t*)scb->hscb - scb->hscb_map->vaddr,
+ /*len*/sizeof(*scb->hscb), op);
+}
+
+static __inline void
+ahd_sync_sglist(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+ if (scb->sg_count == 0)
+ return;
+
+ ahd_dmamap_sync(ahd, ahd->scb_data.sg_dmat,
+ scb->sg_map->dmamap,
+ /*offset*/scb->sg_list_busaddr - ahd_sg_size(ahd),
+ /*len*/ahd_sg_size(ahd) * scb->sg_count, op);
+}
+
+static __inline void
+ahd_sync_sense(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+ ahd_dmamap_sync(ahd, ahd->scb_data.sense_dmat,
+ scb->sense_map->dmamap,
+ /*offset*/scb->sense_busaddr,
+ /*len*/AHD_SENSE_BUFSIZE, op);
+}
+
+static __inline uint32_t
+ahd_targetcmd_offset(struct ahd_softc *ahd, u_int index)
+{
+ return (((uint8_t *)&ahd->targetcmds[index])
+ - (uint8_t *)ahd->qoutfifo);
+}
+
+/*********************** Miscelaneous Support Functions ***********************/
+static __inline void ahd_complete_scb(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_update_residual(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline struct ahd_initiator_tinfo *
+ ahd_fetch_transinfo(struct ahd_softc *ahd,
+ char channel, u_int our_id,
+ u_int remote_id,
+ struct ahd_tmode_tstate **tstate);
+static __inline struct scb*
+ ahd_get_scb(struct ahd_softc *ahd);
+static __inline void ahd_free_scb(struct ahd_softc *ahd, struct scb *scb);
+static __inline uint16_t
+ ahd_inw(struct ahd_softc *ahd, u_int port);
+static __inline void ahd_outw(struct ahd_softc *ahd, u_int port,
+ u_int value);
+static __inline uint32_t
+ ahd_inl(struct ahd_softc *ahd, u_int port);
+static __inline void ahd_outl(struct ahd_softc *ahd, u_int port,
+ uint32_t value);
+static __inline uint64_t
+ ahd_inq(struct ahd_softc *ahd, u_int port);
+static __inline void ahd_outq(struct ahd_softc *ahd, u_int port,
+ uint64_t value);
+static __inline u_int ahd_get_scbptr(struct ahd_softc *ahd);
+static __inline void ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr);
+static __inline u_int ahd_get_hnscb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_get_hescb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_get_snscb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_get_sescb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_get_sdscb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_inb_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline u_int ahd_inw_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline uint32_t
+ ahd_inl_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline void ahd_swap_with_next_hscb(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb);
+static __inline uint8_t *
+ ahd_get_sense_buf(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline uint32_t
+ ahd_get_sense_bufaddr(struct ahd_softc *ahd,
+ struct scb *scb);
+
+static __inline void
+ahd_complete_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ uint32_t sgptr;
+
+ sgptr = ahd_le32toh(scb->hscb->sgptr);
+ if ((sgptr & SG_STATUS_VALID) != 0)
+ ahd_handle_scb_status(ahd, scb);
+ else
+ ahd_done(ahd, scb);
+}
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static __inline void
+ahd_update_residual(struct ahd_softc *ahd, struct scb *scb)
+{
+ uint32_t sgptr;
+
+ sgptr = ahd_le32toh(scb->hscb->sgptr);
+ if ((sgptr & SG_STATUS_VALID) != 0)
+ ahd_calc_residual(ahd, scb);
+}
+
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+static __inline struct ahd_initiator_tinfo *
+ahd_fetch_transinfo(struct ahd_softc *ahd, char channel, u_int our_id,
+ u_int remote_id, struct ahd_tmode_tstate **tstate)
+{
+ /*
+ * Transfer data structures are stored from the perspective
+ * of the target role. Since the parameters for a connection
+ * in the initiator role to a given target are the same as
+ * when the roles are reversed, we pretend we are the target.
+ */
+ if (channel == 'B')
+ our_id += 8;
+ *tstate = ahd->enabled_targets[our_id];
+ return (&(*tstate)->transinfo[remote_id]);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+static __inline struct scb *
+ahd_get_scb(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+
+ if ((scb = SLIST_FIRST(&ahd->scb_data.free_scbs)) == NULL) {
+ ahd_alloc_scbs(ahd);
+ scb = SLIST_FIRST(&ahd->scb_data.free_scbs);
+ if (scb == NULL)
+ return (NULL);
+ }
+ SLIST_REMOVE_HEAD(&ahd->scb_data.free_scbs, links.sle);
+ return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+static __inline void
+ahd_free_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+
+ /* Clean up for the next user */
+ ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = NULL;
+ scb->flags = SCB_FREE;
+ scb->hscb->control = 0;
+
+ SLIST_INSERT_HEAD(&ahd->scb_data.free_scbs, scb, links.sle);
+
+ /* Notify the OSM that a resource is now available. */
+ ahd_platform_scb_free(ahd, scb);
+}
+
+static __inline uint16_t
+ahd_inw(struct ahd_softc *ahd, u_int port)
+{
+ return ((ahd_inb(ahd, port+1) << 8) | ahd_inb(ahd, port));
+}
+
+static __inline void
+ahd_outw(struct ahd_softc *ahd, u_int port, u_int value)
+{
+ ahd_outb(ahd, port, value & 0xFF);
+ ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
+}
+
+static __inline uint32_t
+ahd_inl(struct ahd_softc *ahd, u_int port)
+{
+ return ((ahd_inb(ahd, port))
+ | (ahd_inb(ahd, port+1) << 8)
+ | (ahd_inb(ahd, port+2) << 16)
+ | (ahd_inb(ahd, port+3) << 24));
+}
+
+static __inline void
+ahd_outl(struct ahd_softc *ahd, u_int port, uint32_t value)
+{
+ ahd_outb(ahd, port, (value) & 0xFF);
+ ahd_outb(ahd, port+1, ((value) >> 8) & 0xFF);
+ ahd_outb(ahd, port+2, ((value) >> 16) & 0xFF);
+ ahd_outb(ahd, port+3, ((value) >> 24) & 0xFF);
+}
+
+static __inline uint64_t
+ahd_inq(struct ahd_softc *ahd, u_int port)
+{
+ return ((ahd_inb(ahd, port))
+ | (ahd_inb(ahd, port+1) << 8)
+ | (ahd_inb(ahd, port+2) << 16)
+ | (ahd_inb(ahd, port+3) << 24)
+ | (((uint64_t)ahd_inb(ahd, port+4)) << 32)
+ | (((uint64_t)ahd_inb(ahd, port+5)) << 40)
+ | (((uint64_t)ahd_inb(ahd, port+6)) << 48)
+ | (((uint64_t)ahd_inb(ahd, port+7)) << 56));
+}
+
+static __inline void
+ahd_outq(struct ahd_softc *ahd, u_int port, uint64_t value)
+{
+ ahd_outb(ahd, port, value & 0xFF);
+ ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
+ ahd_outb(ahd, port+2, (value >> 16) & 0xFF);
+ ahd_outb(ahd, port+3, (value >> 24) & 0xFF);
+ ahd_outb(ahd, port+4, (value >> 32) & 0xFF);
+ ahd_outb(ahd, port+5, (value >> 40) & 0xFF);
+ ahd_outb(ahd, port+6, (value >> 48) & 0xFF);
+ ahd_outb(ahd, port+7, (value >> 56) & 0xFF);
+}
+
+static __inline u_int
+ahd_get_scbptr(struct ahd_softc *ahd)
+{
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ return (ahd_inb(ahd, SCBPTR) | (ahd_inb(ahd, SCBPTR + 1) << 8));
+}
+
+static __inline void
+ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr)
+{
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ ahd_outb(ahd, SCBPTR, scbptr & 0xFF);
+ ahd_outb(ahd, SCBPTR+1, (scbptr >> 8) & 0xFF);
+}
+
+static __inline u_int
+ahd_get_hnscb_qoff(struct ahd_softc *ahd)
+{
+ return (ahd_inw_atomic(ahd, HNSCB_QOFF));
+}
+
+static __inline void
+ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ ahd_outw_atomic(ahd, HNSCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_hescb_qoff(struct ahd_softc *ahd)
+{
+ return (ahd_inb(ahd, HESCB_QOFF));
+}
+
+static __inline void
+ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ ahd_outb(ahd, HESCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_snscb_qoff(struct ahd_softc *ahd)
+{
+ u_int oldvalue;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ oldvalue = ahd_inw(ahd, SNSCB_QOFF);
+ ahd_outw(ahd, SNSCB_QOFF, oldvalue);
+ return (oldvalue);
+}
+
+static __inline void
+ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ ahd_outw(ahd, SNSCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_sescb_qoff(struct ahd_softc *ahd)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ return (ahd_inb(ahd, SESCB_QOFF));
+}
+
+static __inline void
+ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ ahd_outb(ahd, SESCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_sdscb_qoff(struct ahd_softc *ahd)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ return (ahd_inb(ahd, SDSCB_QOFF) | (ahd_inb(ahd, SDSCB_QOFF + 1) << 8));
+}
+
+static __inline void
+ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ ahd_outb(ahd, SDSCB_QOFF, value & 0xFF);
+ ahd_outb(ahd, SDSCB_QOFF+1, (value >> 8) & 0xFF);
+}
+
+static __inline u_int
+ahd_inb_scbram(struct ahd_softc *ahd, u_int offset)
+{
+ u_int value;
+
+ /*
+ * Workaround PCI-X Rev A. hardware bug.
+ * After a host read of SCB memory, the chip
+ * may become confused into thinking prefetch
+ * was required. This starts the discard timer
+ * running and can cause an unexpected discard
+ * timer interrupt. The work around is to read
+ * a normal register prior to the exhaustion of
+ * the discard timer. The mode pointer register
+ * has no side effects and so serves well for
+ * this purpose.
+ *
+ * Razor #528
+ */
+ value = ahd_inb(ahd, offset);
+ ahd_inb(ahd, MODE_PTR);
+ return (value);
+}
+
+static __inline u_int
+ahd_inw_scbram(struct ahd_softc *ahd, u_int offset)
+{
+ return (ahd_inb_scbram(ahd, offset)
+ | (ahd_inb_scbram(ahd, offset+1) << 8));
+}
+
+static __inline uint32_t
+ahd_inl_scbram(struct ahd_softc *ahd, u_int offset)
+{
+ return (ahd_inb_scbram(ahd, offset)
+ | (ahd_inb_scbram(ahd, offset+1) << 8)
+ | (ahd_inb_scbram(ahd, offset+2) << 16)
+ | (ahd_inb_scbram(ahd, offset+3) << 24));
+}
+
+static __inline struct scb *
+ahd_lookup_scb(struct ahd_softc *ahd, u_int tag)
+{
+ struct scb* scb;
+
+ if (tag >= AHD_SCB_MAX)
+ return (NULL);
+ scb = ahd->scb_data.scbindex[tag];
+ if (scb != NULL)
+ ahd_sync_scb(ahd, scb,
+ BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+ return (scb);
+}
+
+static __inline void
+ahd_swap_with_next_hscb(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct hardware_scb *q_hscb;
+ uint32_t saved_hscb_busaddr;
+
+ /*
+ * Our queuing method is a bit tricky. The card
+ * knows in advance which HSCB (by address) to download,
+ * and we can't disappoint it. To achieve this, the next
+ * SCB to download is saved off in ahd->next_queued_scb.
+ * When we are called to queue "an arbitrary scb",
+ * we copy the contents of the incoming HSCB to the one
+ * the sequencer knows about, swap HSCB pointers and
+ * finally assign the SCB to the tag indexed location
+ * in the scb_array. This makes sure that we can still
+ * locate the correct SCB by SCB_TAG.
+ */
+ q_hscb = ahd->next_queued_scb->hscb;
+ saved_hscb_busaddr = q_hscb->hscb_busaddr;
+ memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
+ q_hscb->hscb_busaddr = saved_hscb_busaddr;
+ q_hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;
+
+ /* Now swap HSCB pointers. */
+ ahd->next_queued_scb->hscb = scb->hscb;
+ scb->hscb = q_hscb;
+
+ /* Now define the mapping from tag to SCB in the scbindex */
+/* XXX This should be constant now. Can we avoid the mapping? */
+ ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+static __inline void
+ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ ahd_swap_with_next_hscb(ahd, scb);
+
+ if (SCBID_IS_NULL(SCB_GET_TAG(scb)))
+ panic("Attempt to queue invalid SCB tag %x\n",
+ SCB_GET_TAG(scb));
+
+ /*
+ * Keep a history of SCBs we've downloaded in the qinfifo.
+ */
+ ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
+ ahd->qinfifonext++;
+
+ if (scb->sg_count != 0)
+ ahd_setup_data_scb(ahd, scb);
+ else
+ ahd_setup_noxfer_scb(ahd, scb);
+ ahd_setup_scb_common(ahd, scb);
+
+ /*
+ * Make sure our data is consistant from the
+ * perspective of the adapter.
+ */
+ ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_QUEUE) != 0) {
+ printf("%s: Queueing SCB 0x%x bus addr 0x%x - 0x%x%x/0x%x\n",
+ ahd_name(ahd),
+ SCB_GET_TAG(scb), scb->hscb->hscb_busaddr,
+ (u_int)((scb->hscb->dataptr >> 32) & 0xFFFFFFFF),
+ (u_int)(scb->hscb->dataptr & 0xFFFFFFFF),
+ scb->hscb->datacnt);
+ }
+#endif
+ /* Tell the adapter about the newly queued SCB */
+ ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+}
+
+static __inline uint8_t *
+ahd_get_sense_buf(struct ahd_softc *ahd, struct scb *scb)
+{
+ return (scb->sense_data);
+}
+
+static __inline uint32_t
+ahd_get_sense_bufaddr(struct ahd_softc *ahd, struct scb *scb)
+{
+ return (scb->sense_busaddr);
+}
+
+/************************** Interrupt Processing ******************************/
+static __inline void ahd_sync_qoutfifo(struct ahd_softc *ahd, int op);
+static __inline void ahd_sync_tqinfifo(struct ahd_softc *ahd, int op);
+static __inline u_int ahd_check_cmdcmpltqueues(struct ahd_softc *ahd);
+static __inline void ahd_intr(struct ahd_softc *ahd);
+
+static __inline void
+ahd_sync_qoutfifo(struct ahd_softc *ahd, int op)
+{
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap,
+ /*offset*/0, /*len*/AHC_SCB_MAX * sizeof(uint16_t), op);
+}
+
+static __inline void
+ahd_sync_tqinfifo(struct ahd_softc *ahd, int op)
+{
+#ifdef AHD_TARGET_MODE
+ if ((ahd->flags & AHD_TARGETROLE) != 0) {
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap,
+ ahd_targetcmd_offset(ahd, 0),
+ sizeof(struct target_cmd) * AHD_TMODE_CMDS,
+ op);
+ }
+#endif
+}
+
+/*
+ * See if the firmware has posted any completed commands
+ * into our in-core command complete fifos.
+ */
+#define AHD_RUN_QOUTFIFO 0x1
+#define AHD_RUN_TQINFIFO 0x2
+static __inline u_int
+ahd_check_cmdcmpltqueues(struct ahd_softc *ahd)
+{
+ u_int retval;
+
+ retval = 0;
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap,
+ /*offset*/ahd->qoutfifonext, /*len*/2,
+ BUS_DMASYNC_POSTREAD);
+ if (ahd->qoutfifo[ahd->qoutfifonext] != SCB_LIST_NULL_LE)
+ retval |= AHD_RUN_QOUTFIFO;
+#ifdef AHD_TARGET_MODE
+ if ((ahd->flags & AHD_TARGETROLE) != 0
+ && (ahd->flags & AHD_TQINFIFO_BLOCKED) == 0) {
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap,
+ ahd_targetcmd_offset(ahd, ahd->tqinfifofnext),
+ /*len*/sizeof(struct target_cmd),
+ BUS_DMASYNC_POSTREAD);
+ if (ahd->targetcmds[ahd->tqinfifonext].cmd_valid != 0)
+ retval |= AHD_RUN_TQINFIFO;
+ }
+#endif
+ return (retval);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+static __inline void
+ahd_intr(struct ahd_softc *ahd)
+{
+ u_int intstat;
+
+ /*
+ * Instead of directly reading the interrupt status register,
+ * infer the cause of the interrupt by checking our in-core
+ * completion queues. This avoids a costly PCI bus read in
+ * most cases.
+ */
+ if ((ahd->flags & AHD_ALL_INTERRUPTS) == 0
+ && (ahd_check_cmdcmpltqueues(ahd) != 0))
+ intstat = CMDCMPLT;
+ else
+ intstat = ahd_inb(ahd, INTSTAT);
+
+ if (intstat & CMDCMPLT) {
+ ahd_outb(ahd, CLRINT, CLRCMDINT);
+
+ /*
+ * Ensure that the chip sees that we've cleared
+ * this interrupt before we walk the output fifo.
+ * Otherwise, we may, due to posted bus writes,
+ * clear the interrupt after we finish the scan,
+ * and after the sequencer has added new entries
+ * and asserted the interrupt again.
+ */
+ ahd_flush_device_writes(ahd);
+ ahd_run_qoutfifo(ahd);
+#ifdef AHD_TARGET_MODE
+ if ((ahd->flags & AHD_TARGETROLE) != 0)
+ ahd_run_tqinfifo(ahd, /*paused*/FALSE);
+#endif
+ }
+
+ if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0)
+ /* Hot eject */
+ return;
+
+ if ((intstat & INT_PEND) == 0)
+ return;
+
+ if (intstat & HWERRINT) {
+ ahd_handle_hwerrint(ahd);
+ return;
+ }
+
+ if ((intstat & (PCIINT|SPLTINT)) != 0) {
+ ahd->bus_intr(ahd);
+ return;
+ }
+
+ if ((intstat & SEQINT) != 0)
+ ahd_handle_seqint(ahd, intstat);
+
+ if ((intstat & SCSIINT) != 0)
+ ahd_handle_scsiint(ahd, intstat);
+}
+
+#endif /* _AIC79XX_INLINE_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_osm.c b/xen/drivers/scsi/aic7xxx/aic79xx_osm.c
new file mode 100644
index 0000000000..9d3c4c14ac
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -0,0 +1,2927 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#36 $
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-2000 Justin T. Gibbs.
+ * Copyright (c) 1997-1999 Doug Ledford
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/*
+ * This is the only file where module.h should
+ * embed module global version info.
+ */
+#define AHD_MODVERSION_FILE
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+#include <linux/init.h> /* __setup */
+#endif
+
+#include "../sd.h" /* For geometry detection */
+
+#include <linux/mm.h> /* For fetching system memory size */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+/*
+ * Lock protecting manipulation of the ahd softc list.
+ */
+spinlock_t ahd_list_spinlock;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+struct proc_dir_entry proc_scsi_aic79xx = {
+ PROC_SCSI_AIC79XX, 7, "aic79xx",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2,
+ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+/*
+ * Set this to the delay in seconds after SCSI bus reset.
+ * Note, we honor this only for the initial bus reset.
+ * The scsi error recovery code performs its own bus settle
+ * delay handling for error recovery actions.
+ */
+#ifdef CONFIG_AIC79XX_RESET_DELAY_MS
+#define AIC79XX_RESET_DELAY CONFIG_AIC79XX_RESET_DELAY_MS
+#else
+#define AIC79XX_RESET_DELAY 5000
+#endif
+
+/*
+ * To change the default number of tagged transactions allowed per-device,
+ * add a line to the lilo.conf file like:
+ * append="aic79xx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * The tag_commands is an array of 16 to allow for wide and twin adapters.
+ * Twin adapters will use indexes 0-7 for channel 0, and indexes 8-15
+ * for channel 1.
+ */
+typedef struct {
+ uint16_t tag_commands[16]; /* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Modify this as you see fit for your system.
+ *
+ * 0 tagged queuing disabled
+ * 1 <= n <= 253 n == max tags ever dispatched.
+ *
+ * The driver will throttle the number of commands dispatched to a
+ * device if it returns queue full. For devices with a fixed maximum
+ * queue depth, the driver will eventually determine this depth and
+ * lock it in (a console message is printed to indicate that a lock
+ * has occurred). On some devices, queue full is returned for a temporary
+ * resource shortage. These devices will return queue full at varying
+ * depths. The driver will throttle back when the queue fulls occur and
+ * attempt to slowly increase the depth over time as the device recovers
+ * from the resource shortage.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic79xx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to attempt to use up to 64 tags for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3. It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ * to modify in order to change things is just below this comment block.
+adapter_tag_info_t aic79xx_tag_info[] =
+{
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}},
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+#ifdef CONFIG_AIC79XX_CMDS_PER_DEVICE
+#define AIC79XX_CMDS_PER_DEVICE CONFIG_AIC79XX_CMDS_PER_DEVICE
+#else
+#define AIC79XX_CMDS_PER_DEVICE AHD_MAX_QUEUE
+#endif
+
+#define AIC79XX_CONFIGED_TAG_COMMANDS { \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE \
+}
+
+/*
+ * By default, use the number of commands specified by
+ * the users kernel configuration.
+ */
+static adapter_tag_info_t aic79xx_tag_info[] =
+{
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS}
+};
+
+/*
+ * By default, read streaming is disabled. In theory,
+ * read streaming should enhance performance, but early
+ * U320 drive firmware actually performs slower with
+ * read streaming enabled.
+ */
+#ifdef CONFIG_AIC79XX_ENABLE_RD_STRM
+#define AIC79XX_CONFIGED_RD_STRM 0xFFFF
+#else
+#define AIC79XX_CONFIGED_RD_STRM 0
+#endif
+
+static uint16_t aic79xx_rd_strm_info[] =
+{
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW DID_ERROR
+
+void
+ahd_print_path(struct ahd_softc *ahd, struct scb *scb)
+{
+ printk("(scsi%d:%c:%d:%d): ",
+ ahd->platform_data->host->host_no,
+ scb != NULL ? SCB_GET_CHANNEL(ahd, scb) : 'X',
+ scb != NULL ? SCB_GET_TARGET(ahd, scb) : -1,
+ scb != NULL ? SCB_GET_LUN(scb) : -1);
+}
+
+/*
+ * XXX - these options apply unilaterally to _all_ adapters
+ * cards in the system. This should be fixed. Exceptions to this
+ * rule are noted in the comments.
+ */
+
+/*
+ * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static uint32_t aic79xx_no_reset;
+
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips. The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect. Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from. The only exceptions to this are when a controller
+ * has its BIOS disabled. So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number. We also force
+ * all controllers with their BIOS disabled to the end of the list. This
+ * works on *almost* all computers. Where it doesn't work, we have this
+ * option. Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end. That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic79xx_reverse_scan = 0;
+
+/*
+ * Should we force EXTENDED translation on a controller.
+ * 0 == Use whatever is in the SEEPROM or default to off
+ * 1 == Use whatever is in the SEEPROM or default to on
+ */
+static uint32_t aic79xx_extended = 0;
+
+/*
+ * PCI bus parity checking of the Adaptec controllers. This is somewhat
+ * dubious at best. To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ * 0 = Shut off PCI parity check
+ * -1 = Normal polarity pci parity checking
+ * 1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt. So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number. That will invert the 0 which will result in
+ * -1.
+ */
+static int aic79xx_pci_parity = 0;
+
+/*
+ * aic79xx_detect() has been run, so register all device arrivals
+ * immediately with the system rather than deferring to the sorted
+ * attachment performed by aic79xx_detect().
+ */
+int aic79xx_detect_complete;
+
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ * 0 - 256ms
+ * 1 - 128ms
+ * 2 - 64ms
+ * 3 - 32ms
+ * We default to 256ms because some older devices need a longer time
+ * to respond to initial selection.
+ */
+static int aic79xx_seltime = 0x00;
+
+/*
+ * Certain devices do not perform any aging on commands. Should the
+ * device be saturated by commands in one portion of the disk, it is
+ * possible for transactions on far away sectors to never be serviced.
+ * To handle these devices, we can periodically send an ordered tag to
+ * force all outstanding transactions to be serviced prior to a new
+ * transaction.
+ */
+int aic79xx_periodic_otag;
+
+/*
+ * Module information and settable options.
+ */
+#ifdef MODULE
+static char *aic79xx = NULL;
+/*
+ * Just in case someone uses commas to separate items on the insmod
+ * command line, we define a dummy buffer here to avoid having insmod
+ * write wild stuff into our code segment
+ */
+static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
+
+MODULE_AUTHOR("Maintainer: Justin T. Gibbs <gibbs@scsiguy.com>");
+MODULE_DESCRIPTION("Adaptec Aic77XX/78XX SCSI Host Bus Adapter driver");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+MODULE_PARM(aic79xx, "s");
+MODULE_PARM_DESC(aic79xx, "period delimited, options string.
+ verbose Enable verbose/diagnostic logging
+ debug Bitmask of debug values to enable
+ no_reset Supress initial bus resets
+ extended Enable extended geometry on all controllers
+ periodic_otag Send an ordered tagged transaction periodically
+ to prevent tag starvation. This may be
+ required by some older disk drives/RAID arrays.
+ reverse_scan Sort PCI devices highest Bus/Slot to lowest
+ tag_info:<tag_str> Set per-target tag depth
+ rd_strm:<rd_strm_masks> Set per-target read streaming setting.
+ seltime:<int> Selection Timeout(0/256ms,1/128ms,2/64ms,3/32ms)
+
+ Sample /etc/modules.conf line:
+ Enable verbose logging
+ Set tag depth on Controller 2/Target 2 to 10 tags
+ Shorten the selection timeout to 128ms from its default of 256
+
+ options aic79xx='\"verbose.tag_info:{{}.{}.{..10}}.seltime:1\"'
+
+ Sample /etc/modules.conf line:
+ Change Read Streaming for Controller's 2 and 3
+
+ options aic79xx='\"rd_strm:{..0xFFF0.0xC0F0}\"'
+");
+#endif
+
+static void ahd_linux_handle_scsi_status(struct ahd_softc *,
+ struct ahd_linux_device *,
+ struct scb *);
+static void ahd_linux_filter_command(struct ahd_softc*, Scsi_Cmnd*,
+ struct scb*);
+static void ahd_linux_dev_timed_unfreeze(u_long arg);
+#if NO_YET
+static void ahd_linux_sem_timeout(u_long arg);
+static int ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag);
+#endif
+static void ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd);
+static void ahd_linux_select_queue_depth(struct Scsi_Host *host,
+ Scsi_Device *scsi_devs);
+static u_int ahd_linux_user_tagdepth(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_linux_device_queue_depth(struct ahd_softc *ahd,
+ Scsi_Device *device);
+static struct ahd_linux_target* ahd_linux_alloc_target(struct ahd_softc*,
+ u_int, u_int);
+static void ahd_linux_free_target(struct ahd_softc*,
+ struct ahd_linux_target*);
+static struct ahd_linux_device* ahd_linux_alloc_device(struct ahd_softc*,
+ struct ahd_linux_target*,
+ u_int);
+static void ahd_linux_free_device(struct ahd_softc*,
+ struct ahd_linux_device*);
+static void ahd_linux_run_device_queue(struct ahd_softc*,
+ struct ahd_linux_device*);
+static void ahd_linux_setup_tag_info(char *p, char *end);
+static void ahd_linux_setup_rd_strm_info(char *p, char *end);
+static int ahd_linux_next_unit(void);
+static void ahd_runq_tasklet(unsigned long data);
+static int ahd_linux_halt(struct notifier_block *nb, u_long event, void *buf);
+
+static __inline struct ahd_linux_device*
+ ahd_linux_get_device(struct ahd_softc *ahd, u_int channel,
+ u_int target, u_int lun, int alloc);
+static __inline void ahd_linux_queue_cmd_complete(struct ahd_softc *ahd,
+ Scsi_Cmnd *cmd);
+static __inline void ahd_linux_run_complete_queue(struct ahd_softc *ahd,
+ struct ahd_cmd *acmd);
+static __inline void ahd_linux_check_device_queue(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev);
+static __inline struct ahd_linux_device *
+ ahd_linux_next_device_to_run(struct ahd_softc *ahd);
+static __inline void ahd_linux_run_device_queues(struct ahd_softc *ahd);
+static __inline void ahd_linux_sniff_command(struct ahd_softc*, Scsi_Cmnd*,
+ struct scb*);
+static __inline void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*);
+
+static __inline int ahd_linux_map_seg(struct ahd_softc *ahd, struct scb *scb,
+ struct ahd_dma_seg *sg,
+ bus_addr_t addr, bus_size_t len);
+
+static __inline struct ahd_linux_device*
+ahd_linux_get_device(struct ahd_softc *ahd, u_int channel, u_int target,
+ u_int lun, int alloc)
+{
+ struct ahd_linux_target *targ;
+ struct ahd_linux_device *dev;
+ u_int target_offset;
+
+ target_offset = target;
+ if (channel != 0)
+ target_offset += 8;
+ targ = ahd->platform_data->targets[target_offset];
+ if (targ == NULL) {
+ if (alloc != 0) {
+ targ = ahd_linux_alloc_target(ahd, channel, target);
+ if (targ == NULL)
+ return (NULL);
+ } else
+ return (NULL);
+ }
+ dev = targ->devices[lun];
+ if (dev == NULL && alloc != 0)
+ dev = ahd_linux_alloc_device(ahd, targ, lun);
+ return (dev);
+}
+
+static __inline void
+ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, Scsi_Cmnd *cmd)
+{
+ /*
+ * Typically, the complete queue has very few entries
+ * queued to it before the queue is emptied by
+ * ahd_linux_run_complete_queue, so sorting the entries
+ * by generation number should be inexpensive.
+ * We perform the sort so that commands that complete
+ * with an error are retuned in the order origionally
+ * queued to the controller so that any subsequent retries
+ * are performed in order. The underlying ahd routines do
+ * not guarantee the order that aborted commands will be
+ * returned to us.
+ */
+ struct ahd_completeq *completeq;
+ struct ahd_cmd *list_cmd;
+ struct ahd_cmd *acmd;
+
+ /*
+ * If we want the request requeued, make sure there
+ * are sufficent retries. In the old scsi error code,
+ * we used to be able to specify a result code that
+ * bypassed the retry count. Now we must use this
+ * hack.
+ */
+ if (cmd->result == (CAM_REQUEUE_REQ << 16))
+ cmd->retries--;
+ completeq = &ahd->platform_data->completeq;
+ list_cmd = TAILQ_FIRST(completeq);
+ acmd = (struct ahd_cmd *)cmd;
+ while (list_cmd != NULL
+ && acmd_scsi_cmd(list_cmd).serial_number
+ < acmd_scsi_cmd(acmd).serial_number)
+ list_cmd = TAILQ_NEXT(list_cmd, acmd_links.tqe);
+ if (list_cmd != NULL)
+ TAILQ_INSERT_BEFORE(list_cmd, acmd, acmd_links.tqe);
+ else
+ TAILQ_INSERT_TAIL(completeq, acmd, acmd_links.tqe);
+}
+
+static __inline void
+ahd_linux_run_complete_queue(struct ahd_softc *ahd, struct ahd_cmd *acmd)
+{
+ u_long done_flags;
+
+ ahd_done_lock(ahd, &done_flags);
+ while (acmd != NULL) {
+ Scsi_Cmnd *cmd;
+
+ cmd = &acmd_scsi_cmd(acmd);
+ acmd = TAILQ_NEXT(acmd, acmd_links.tqe);
+ cmd->host_scribble = NULL;
+ cmd->scsi_done(cmd);
+ }
+ ahd_done_unlock(ahd, &done_flags);
+}
+
+static __inline void
+ahd_linux_check_device_queue(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev)
+{
+ if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) != 0
+ && dev->active == 0) {
+ dev->flags &= ~AHD_DEV_FREEZE_TIL_EMPTY;
+ dev->qfrozen--;
+ }
+
+ if (TAILQ_FIRST(&dev->busyq) == NULL
+ || dev->openings == 0 || dev->qfrozen != 0)
+ return;
+
+ ahd_linux_run_device_queue(ahd, dev);
+}
+
+static __inline struct ahd_linux_device *
+ahd_linux_next_device_to_run(struct ahd_softc *ahd)
+{
+
+ if ((ahd->flags & AHD_RESOURCE_SHORTAGE) != 0
+ || ahd->platform_data->qfrozen != 0)
+ return (NULL);
+ return (TAILQ_FIRST(&ahd->platform_data->device_runq));
+}
+
+static __inline void
+ahd_linux_run_device_queues(struct ahd_softc *ahd)
+{
+ struct ahd_linux_device *dev;
+
+ while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) {
+ TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links);
+ dev->flags &= ~AHD_DEV_ON_RUN_LIST;
+ ahd_linux_check_device_queue(ahd, dev);
+ }
+}
+
+static __inline void
+ahd_linux_sniff_command(struct ahd_softc *ahd, Scsi_Cmnd *cmd, struct scb *scb)
+{
+ /*
+ * Determine whether we care to filter
+ * information out of this command. If so,
+ * pass it on to ahd_linux_filter_command() for more
+ * heavy weight processing.
+ */
+ if (cmd->cmnd[0] == INQUIRY)
+ ahd_linux_filter_command(ahd, cmd, scb);
+}
+
+static __inline void
+ahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ Scsi_Cmnd *cmd;
+ int direction;
+
+ cmd = scb->io_ctx;
+ direction = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+ ahd_sync_sglist(ahd, scb, BUS_DMASYNC_POSTWRITE);
+ if (cmd->use_sg != 0) {
+ struct scatterlist *sg;
+
+ sg = (struct scatterlist *)cmd->request_buffer;
+ pci_unmap_sg(ahd->dev_softc, sg, cmd->use_sg, direction);
+ } else if (cmd->request_bufflen != 0) {
+ pci_unmap_single(ahd->dev_softc,
+ scb->platform_data->buf_busaddr,
+ cmd->request_bufflen, direction);
+ }
+}
+
+static __inline int
+ahd_linux_map_seg(struct ahd_softc *ahd, struct scb *scb,
+ struct ahd_dma_seg *sg, bus_addr_t addr, bus_size_t len)
+{
+ int consumed;
+
+ if ((scb->sg_count + 1) > AHD_NSEG)
+ panic("Too few segs for dma mapping. "
+ "Increase AHD_NSEG\n");
+
+ consumed = 1;
+ sg->addr = ahd_htole32(addr & 0xFFFFFFFF);
+ scb->platform_data->xfer_len += len;
+ if (sizeof(bus_addr_t) > 4
+ && (ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+ /*
+ * Due to DAC restrictions, we can't
+ * cross a 4GB boundary.
+ */
+ if ((addr ^ (addr + len - 1)) & ~0xFFFFFFFF) {
+ struct ahd_dma_seg *next_sg;
+ uint32_t next_len;
+
+ printf("Crossed Seg\n");
+ if ((scb->sg_count + 2) > AHD_NSEG)
+ panic("Too few segs for dma mapping. "
+ "Increase AHD_NSEG\n");
+
+ consumed++;
+ next_sg = sg + 1;
+ next_sg->addr = 0;
+ next_len = 0x100000000 - (addr & 0xFFFFFFFF);
+ len -= next_len;
+ next_len |= ((addr >> 8) + 0x1000000) & 0x7F000000;
+ next_sg->len = ahd_htole32(next_len);
+ }
+ len |= (addr >> 8) & 0x7F000000;
+ }
+ sg->len = ahd_htole32(len);
+ return (consumed);
+}
+
+/**************************** Tasklet Handler *********************************/
+
+static void
+ahd_runq_tasklet(unsigned long data)
+{
+ struct ahd_softc* ahd;
+ struct ahd_linux_device *dev;
+ u_long flags;
+
+ ahd = (struct ahd_softc *)data;
+ ahd_lock(ahd, &flags);
+ while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) {
+
+ TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links);
+ dev->flags &= ~AHD_DEV_ON_RUN_LIST;
+ ahd_linux_check_device_queue(ahd, dev);
+ /* Yeild to our interrupt handler */
+ ahd_unlock(ahd, &flags);
+ ahd_lock(ahd, &flags);
+ }
+ ahd_unlock(ahd, &flags);
+}
+
+/************************ Shutdown/halt/reboot hook ***************************/
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+static struct notifier_block ahd_linux_notifier = {
+ ahd_linux_halt, NULL, 0
+};
+
+static int ahd_linux_halt(struct notifier_block *nb, u_long event, void *buf)
+{
+ struct ahd_softc *ahd;
+
+ if (event == SYS_DOWN || event == SYS_HALT) {
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+ ahd_shutdown(ahd);
+ }
+ }
+ return (NOTIFY_OK);
+}
+
+/******************************** Macros **************************************/
+#define BUILD_SCSIID(ahd, cmd) \
+ ((((cmd)->target << TID_SHIFT) & TID) | (ahd)->our_id)
+
+/******************************** Bus DMA *************************************/
+int
+ahd_dma_tag_create(struct ahd_softc *ahd, bus_dma_tag_t parent,
+ bus_size_t alignment, bus_size_t boundary,
+ bus_addr_t lowaddr, bus_addr_t highaddr,
+ bus_dma_filter_t *filter, void *filterarg,
+ bus_size_t maxsize, int nsegments,
+ bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag)
+{
+ bus_dma_tag_t dmat;
+
+ dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT);
+ if (dmat == NULL)
+ return (ENOMEM);
+
+ /*
+ * Linux is very simplistic about DMA memory. For now don't
+ * maintain all specification information. Once Linux supplies
+ * better facilities for doing these operations, or the
+ * needs of this particular driver change, we might need to do
+ * more here.
+ */
+ dmat->alignment = alignment;
+ dmat->boundary = boundary;
+ dmat->maxsize = maxsize;
+ *ret_tag = dmat;
+ return (0);
+}
+
+void
+ahd_dma_tag_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat)
+{
+ free(dmat, M_DEVBUF);
+}
+
+int
+ahd_dmamem_alloc(struct ahd_softc *ahd, bus_dma_tag_t dmat, void** vaddr,
+ int flags, bus_dmamap_t *mapp)
+{
+ bus_dmamap_t map;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT);
+ if (map == NULL)
+ return (ENOMEM);
+ /*
+ * Although we can dma data above 4GB, our
+ * "consistent" memory is below 4GB for
+ * space efficiency reasons (only need a 4byte
+ * address). For this reason, we have to reset
+ * our dma mask when doing allocations.
+ */
+ if (ahd->dev_softc != NULL)
+ ahd_pci_set_dma_mask(ahd->dev_softc, 0xFFFFFFFF);
+ *vaddr = pci_alloc_consistent(ahd->dev_softc,
+ dmat->maxsize, &map->bus_addr);
+ if (ahd->dev_softc != NULL)
+ ahd_pci_set_dma_mask(ahd->dev_softc,
+ ahd->platform_data->hw_dma_mask);
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) */
+ /*
+ * At least in 2.2.14, malloc is a slab allocator so all
+ * allocations are aligned. We assume for these kernel versions
+ * that all allocations will be bellow 4Gig, physically contiguous,
+ * and accessable via DMA by the controller.
+ */
+ map = NULL; /* No additional information to store */
+ *vaddr = malloc(dmat->maxsize, M_DEVBUF, M_NOWAIT);
+#endif
+ if (*vaddr == NULL)
+ return (ENOMEM);
+ *mapp = map;
+ return(0);
+}
+
+void
+ahd_dmamem_free(struct ahd_softc *ahd, bus_dma_tag_t dmat,
+ void* vaddr, bus_dmamap_t map)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ pci_free_consistent(ahd->dev_softc, dmat->maxsize,
+ vaddr, map->bus_addr);
+#else
+ free(vaddr, M_DEVBUF);
+#endif
+}
+
+int
+ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map,
+ void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb,
+ void *cb_arg, int flags)
+{
+ /*
+ * Assume for now that this will only be used during
+ * initialization and not for per-transaction buffer mapping.
+ */
+ bus_dma_segment_t stack_sg;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ stack_sg.ds_addr = map->bus_addr;
+#else
+#define VIRT_TO_BUS(a) (uint32_t)virt_to_bus((void *)(a))
+ stack_sg.ds_addr = VIRT_TO_BUS(buf);
+#endif
+ stack_sg.ds_len = dmat->maxsize;
+ cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0);
+ return (0);
+}
+
+void
+ahd_dmamap_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ /*
+ * The map may is NULL in our < 2.3.X implementation.
+ */
+ if (map != NULL)
+ free(map, M_DEVBUF);
+}
+
+int
+ahd_dmamap_unload(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ /* Nothing to do */
+ return (0);
+}
+
+/********************* Platform Dependent Functions ***************************/
+int
+ahd_softc_comp(struct ahd_softc *lahd, struct ahd_softc *rahd)
+{
+ int value;
+ char primary_channel;
+
+ /*
+ * Under Linux, cards are ordered as follows:
+ * 1) PCI devices with BIOS enabled sorted by bus/slot/func.
+ * 2) All remaining PCI devices sorted by bus/slot/func.
+ */
+ value = (lahd->flags & AHD_BIOS_ENABLED)
+ - (rahd->flags & AHD_BIOS_ENABLED);
+ if (value != 0)
+ /* Controllers with BIOS enabled have a *higher* priority */
+ return (-value);
+
+ /* Still equal. Sort by bus/slot/func. */
+ if (aic79xx_reverse_scan != 0)
+ value = ahd_get_pci_bus(rahd->dev_softc)
+ - ahd_get_pci_bus(lahd->dev_softc);
+ else
+ value = ahd_get_pci_bus(lahd->dev_softc)
+ - ahd_get_pci_bus(rahd->dev_softc);
+ if (value != 0)
+ return (value);
+ if (aic79xx_reverse_scan != 0)
+ value = ahd_get_pci_slot(rahd->dev_softc)
+ - ahd_get_pci_slot(lahd->dev_softc);
+ else
+ value = ahd_get_pci_slot(lahd->dev_softc)
+ - ahd_get_pci_slot(rahd->dev_softc);
+ if (value != 0)
+ return (value);
+
+ /*
+ * On multi-function devices, the user can choose
+ * to have function 1 probed before function 0.
+ * Give whichever channel is the primary channel
+ * the lowest priority.
+ */
+ primary_channel = (lahd->flags & AHD_PRIMARY_CHANNEL) + 'A';
+ value = 1;
+ if (lahd->channel == primary_channel)
+ value = -1;
+ return (value);
+}
+
+static void
+ahd_linux_setup_tag_info(char *p, char *end)
+{
+ char *base;
+ char *tok;
+ char *tok_end;
+ char *tok_end2;
+ int i;
+ int instance;
+ int targ;
+ int done;
+ char tok_list[] = {'.', ',', '{', '}', '\0'};
+
+ if (*p != ':')
+ return;
+
+ instance = -1;
+ targ = -1;
+ done = FALSE;
+ base = p;
+ /* Forward us just past the ':' */
+ tok = base + 1;
+ tok_end = strchr(tok, '\0');
+ if (tok_end < end)
+ *tok_end = ',';
+ while (!done) {
+ switch (*tok) {
+ case '{':
+ if (instance == -1)
+ instance = 0;
+ else if (targ == -1)
+ targ = 0;
+ tok++;
+ break;
+ case '}':
+ if (targ != -1)
+ targ = -1;
+ else if (instance != -1)
+ instance = -1;
+ tok++;
+ break;
+ case ',':
+ case '.':
+ if (instance == -1)
+ done = TRUE;
+ else if (targ >= 0)
+ targ++;
+ else if (instance >= 0)
+ instance++;
+ if ((targ >= AHD_NUM_TARGETS) ||
+ (instance >= NUM_ELEMENTS(aic79xx_tag_info)))
+ done = TRUE;
+ tok++;
+ if (!done) {
+ base = tok;
+ }
+ break;
+ case '\0':
+ done = TRUE;
+ break;
+ default:
+ done = TRUE;
+ tok_end = strchr(tok, '\0');
+ for (i = 0; tok_list[i]; i++) {
+ tok_end2 = strchr(tok, tok_list[i]);
+ if ((tok_end2) && (tok_end2 < tok_end)) {
+ tok_end = tok_end2;
+ done = FALSE;
+ }
+ }
+ if ((instance >= 0) && (targ >= 0)
+ && (instance < NUM_ELEMENTS(aic79xx_tag_info))
+ && (targ < AHD_NUM_TARGETS)) {
+ aic79xx_tag_info[instance].tag_commands[targ] =
+ simple_strtoul(tok, NULL, 0) & 0xff;
+ }
+ tok = tok_end;
+ break;
+ }
+ }
+ while ((p != base) && (p != NULL))
+ p = strtok(NULL, ",.");
+}
+
+static void
+ahd_linux_setup_rd_strm_info(char *p, char *end)
+{
+ char *base;
+ char *tok;
+ char *tok_end;
+ char *tok_end2;
+ int i;
+ int instance;
+ int targ;
+ int done;
+ char tok_list[] = {'.', ',', '{', '}', '\0'};
+
+ if (*p != ':')
+ return;
+
+ instance = -1;
+ targ = -1;
+ done = FALSE;
+ base = p;
+ /* Forward us just past the ':' */
+ tok = base + 1;
+ tok_end = strchr(tok, '\0');
+ if (tok_end < end)
+ *tok_end = ',';
+ while (!done) {
+ switch (*tok) {
+ case '{':
+ if (instance == -1)
+ instance = 0;
+ tok++;
+ break;
+ case '}':
+ if (instance != -1)
+ instance = -1;
+ tok++;
+ break;
+ case ',':
+ case '.':
+ if (instance == -1)
+ done = TRUE;
+ else if (instance >= 0)
+ instance++;
+ if (instance >= NUM_ELEMENTS(aic79xx_rd_strm_info))
+ done = TRUE;
+ tok++;
+ if (!done) {
+ base = tok;
+ }
+ break;
+ case '\0':
+ done = TRUE;
+ break;
+ default:
+ done = TRUE;
+ tok_end = strchr(tok, '\0');
+ for (i = 0; tok_list[i]; i++) {
+ tok_end2 = strchr(tok, tok_list[i]);
+ if ((tok_end2) && (tok_end2 < tok_end)) {
+ tok_end = tok_end2;
+ done = FALSE;
+ }
+ }
+ if ((instance >= 0)
+ && (instance < NUM_ELEMENTS(aic79xx_tag_info))) {
+ aic79xx_rd_strm_info[instance] =
+ simple_strtoul(tok, NULL, 0) & 0xffff;
+ }
+ tok = tok_end;
+ break;
+ }
+ }
+ while ((p != base) && (p != NULL))
+ p = strtok(NULL, ",.");
+}
+
+/*
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic79xx=stpwlev:1,extended
+ */
+int
+aic79xx_setup(char *s)
+{
+ int i, n;
+ char *p;
+ char *end;
+
+ static struct {
+ const char *name;
+ uint32_t *flag;
+ } options[] = {
+ { "extended", &aic79xx_extended },
+ { "no_reset", &aic79xx_no_reset },
+ { "verbose", &aic79xx_verbose },
+#ifdef AHD_DEBUG
+ { "debug", &ahd_debug },
+#endif
+ { "reverse_scan", &aic79xx_reverse_scan },
+ { "periodic_otag", &aic79xx_periodic_otag },
+ { "pci_parity", &aic79xx_pci_parity },
+ { "seltime", &aic79xx_seltime },
+ { "tag_info", NULL },
+ { "rd_strm", NULL }
+ };
+
+ end = strchr(s, '\0');
+
+ for (p = strtok(s, ",."); p; p = strtok(NULL, ",.")) {
+ for (i = 0; i < NUM_ELEMENTS(options); i++) {
+ n = strlen(options[i].name);
+
+ if (strncmp(options[i].name, p, n) != 0)
+ continue;
+
+ if (strncmp(p, "tag_info", n) == 0) {
+ ahd_linux_setup_tag_info(p + n, end);
+ } else if (strncmp(p, "rd_strm", n) == 0) {
+ ahd_linux_setup_rd_strm_info(p + n, end);
+ } else if (p[n] == ':') {
+ *(options[i].flag) =
+ simple_strtoul(p + n + 1, NULL, 0);
+ } else if (!strncmp(p, "verbose", n)) {
+ *(options[i].flag) = 1;
+ } else {
+ *(options[i].flag) = ~(*(options[i].flag));
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+__setup("aic79xx=", aic79xx_setup);
+#endif
+
+int aic79xx_verbose;
+
+/*
+ * Try to detect an Adaptec 79XX controller.
+ */
+int
+ahd_linux_detect(Scsi_Host_Template *template)
+{
+ struct ahd_softc *ahd;
+ int found;
+
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+
+ /*
+ * Sanity checking of Linux SCSI data structures so
+ * that some of our hacks^H^H^H^H^Hassumptions aren't
+ * violated.
+ */
+ if (offsetof(struct ahd_cmd_internal, end)
+ > offsetof(struct scsi_cmnd, host_scribble)) {
+ printf("ahd_linux_detect: SCSI data structures changed.\n");
+ printf("ahd_linux_detect: Unable to attach\n");
+ return (0);
+ }
+#ifdef MODULE
+ /*
+ * If we've been passed any parameters, process them now.
+ */
+ if (aic79xx)
+ aic79xx_setup(aic79xx);
+ if (dummy_buffer[0] != 'P')
+ printk(KERN_WARNING
+"aic79xx: Please read the file /usr/src/linux/drivers/scsi/README.aic79xx\n"
+"aic79xx: to see the proper way to specify options to the aic79xx module\n"
+"aic79xx: Specifically, don't use any commas when passing arguments to\n"
+"aic79xx: insmod or else it might trash certain memory areas.\n");
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+ template->proc_name = "aic79xx";
+#else
+ template->proc_dir = &proc_scsi_aic79xx;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7)
+ /*
+ * We can only map 16MB per-SG
+ * so create a sector limit of
+ * "16MB" in 2K sectors.
+ */
+ template->max_sectors = 8192;
+#endif
+
+ /*
+ * Initialize our softc list lock prior to
+ * probing for any adapters.
+ */
+ ahd_list_lockinit();
+
+#ifdef CONFIG_PCI
+ ahd_linux_pci_probe(template);
+#endif
+
+ /*
+ * Register with the SCSI layer all
+ * controllers we've found.
+ */
+ spin_lock_irq(&io_request_lock);
+ found = 0;
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+
+ if (ahd_linux_register_host(ahd, template) == 0)
+ found++;
+ }
+ aic79xx_detect_complete++;
+ return (found);
+}
+
+int
+ahd_linux_register_host(struct ahd_softc *ahd, Scsi_Host_Template *template)
+{
+ char buf[80];
+ struct Scsi_Host *host;
+ char *new_name;
+ u_long s;
+
+ template->name = ahd->description;
+ host = scsi_register(template, sizeof(struct ahd_softc *));
+ if (host == NULL)
+ return (ENOMEM);
+
+ ahd_lock(ahd, &s);
+ *((struct ahd_softc **)host->hostdata) = ahd;
+ ahd->platform_data->host = host;
+ host->can_queue = AHD_MAX_QUEUE;
+ host->cmd_per_lun = 2;
+ host->sg_tablesize = AHD_NSEG;
+ host->select_queue_depths = ahd_linux_select_queue_depth;
+ host->this_id = ahd->our_id;
+ host->irq = ahd->platform_data->irq;
+ host->max_id = (ahd->features & AHD_WIDE) ? 16 : 8;
+ host->max_lun = AHD_NUM_LUNS;
+ host->max_channel = 0;
+ ahd_set_unit(ahd, ahd_linux_next_unit());
+ sprintf(buf, "scsi%d", host->host_no);
+ new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (new_name != NULL) {
+ strcpy(new_name, buf);
+ ahd_set_name(ahd, new_name);
+ }
+ host->unique_id = ahd->unit;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
+ scsi_set_pci_device(host, ahd->dev_softc);
+#endif
+ ahd_linux_initialize_scsi_bus(ahd);
+ ahd_unlock(ahd, &s);
+ return (0);
+}
+
+uint64_t
+ahd_linux_get_memsize()
+{
+ struct sysinfo si;
+
+ si_meminfo(&si);
+ return (si.totalram << PAGE_SHIFT);
+}
+
+/*
+ * Find the smallest available unit number to use
+ * for a new device. We don't just use a static
+ * count to handle the "repeated hot-(un)plug"
+ * scenario.
+ */
+static int
+ahd_linux_next_unit()
+{
+ struct ahd_softc *ahd;
+ int unit;
+
+ unit = 0;
+retry:
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+ if (ahd->unit == unit) {
+ unit++;
+ goto retry;
+ }
+ }
+ return (unit);
+}
+
+/*
+ * Place the SCSI bus into a known state by either resetting it,
+ * or forcing transfer negotiations on the next command to any
+ * target.
+ */
+void
+ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd)
+{
+ int i;
+ int numtarg;
+
+ i = 0;
+ numtarg = 0;
+
+ if (aic79xx_no_reset != 0)
+ ahd->flags &= ~AHD_RESET_BUS_A;
+
+ if ((ahd->flags & AHD_RESET_BUS_A) != 0)
+ ahd_reset_channel(ahd, 'A', /*initiate_reset*/TRUE);
+ else
+ numtarg = (ahd->features & AHD_WIDE) ? 16 : 8;
+
+ for (; i < numtarg; i++) {
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_id;
+ char channel;
+
+ channel = 'A';
+ our_id = ahd->our_id;
+ target_id = i;
+ tinfo = ahd_fetch_transinfo(ahd, channel, our_id,
+ target_id, &tstate);
+ tinfo->goal = tinfo->user;
+ /*
+ * Don't try negotiations that require PPR messages
+ * until we successfully retrieve Inquiry data.
+ */
+ tinfo->goal.ppr_options = 0;
+ if (tinfo->goal.transport_version > SCSI_REV_2)
+ tinfo->goal.transport_version = SCSI_REV_2;
+ ahd_compile_devinfo(&devinfo, our_id, target_id,
+ CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);
+ ahd_update_neg_request(ahd, &devinfo, tstate,
+ tinfo, /*force*/FALSE);
+ }
+ /* Give the bus some time to recover */
+ if ((ahd->flags & AHD_RESET_BUS_A) != 0) {
+ ahd_freeze_simq(ahd);
+ init_timer(&ahd->platform_data->reset_timer);
+ ahd->platform_data->reset_timer.data = (u_long)ahd;
+ ahd->platform_data->reset_timer.expires =
+ jiffies + (AIC79XX_RESET_DELAY * HZ)/1000;
+ ahd->platform_data->reset_timer.function =
+ (ahd_linux_callback_t *)ahd_release_simq;
+ add_timer(&ahd->platform_data->reset_timer);
+ }
+}
+
+int
+ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg)
+{
+ ahd->platform_data =
+ malloc(sizeof(struct ahd_platform_data), M_DEVBUF, M_NOWAIT);
+ if (ahd->platform_data == NULL)
+ return (ENOMEM);
+ memset(ahd->platform_data, 0, sizeof(struct ahd_platform_data));
+ TAILQ_INIT(&ahd->platform_data->completeq);
+ TAILQ_INIT(&ahd->platform_data->device_runq);
+ ahd->platform_data->irq = AHD_LINUX_NOIRQ;
+ ahd->platform_data->hw_dma_mask = 0xFFFFFFFF;
+ ahd_lockinit(ahd);
+ ahd_done_lockinit(ahd);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ init_MUTEX_LOCKED(&ahd->platform_data->eh_sem);
+#else
+ ahd->platform_data->eh_sem = MUTEX_LOCKED;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_init(&ahd->platform_data->runq_tasklet, ahd_runq_tasklet,
+ (unsigned long)ahd);
+#endif
+ ahd->seltime = (aic79xx_seltime & 0x3) << 4;
+
+ if (TAILQ_EMPTY(&ahd_tailq))
+ register_reboot_notifier(&ahd_linux_notifier);
+ return (0);
+}
+
+void
+ahd_platform_free(struct ahd_softc *ahd)
+{
+ if (ahd->platform_data != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_kill(&ahd->platform_data->runq_tasklet);
+#endif
+ if (ahd->platform_data->host != NULL)
+ scsi_unregister(ahd->platform_data->host);
+ if (ahd->platform_data->irq != AHD_LINUX_NOIRQ)
+ free_irq(ahd->platform_data->irq, ahd);
+ if (ahd->tags[0] == BUS_SPACE_PIO
+ && ahd->bshs[0].ioport != 0)
+ release_region(ahd->bshs[0].ioport, 256);
+ if (ahd->tags[1] == BUS_SPACE_PIO
+ && ahd->bshs[1].ioport != 0)
+ release_region(ahd->bshs[1].ioport, 256);
+ if (ahd->tags[0] == BUS_SPACE_MEMIO
+ && ahd->bshs[0].maddr != NULL) {
+ u_long base_addr;
+
+ base_addr = (u_long)ahd->bshs[0].maddr;
+ base_addr &= PAGE_MASK;
+ iounmap((void *)base_addr);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ release_mem_region(ahd->platform_data->mem_busaddr,
+ 0x1000);
+#endif
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /* XXX Need an instance detach in the PCI code */
+ if (ahd->dev_softc != NULL)
+ ahd->dev_softc->driver = NULL;
+#endif
+ free(ahd->platform_data, M_DEVBUF);
+ }
+ if (TAILQ_EMPTY(&ahd_tailq)) {
+ unregister_reboot_notifier(&ahd_linux_notifier);
+#ifdef CONFIG_PCI
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pci_unregister_driver(&aic79xx_pci_driver);
+#endif
+#endif
+ }
+}
+
+void
+ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
+{
+ ahd_platform_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), SCB_LIST_NULL,
+ ROLE_UNKNOWN, CAM_REQUEUE_REQ);
+}
+
+void
+ahd_platform_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ ahd_queue_alg alg)
+{
+ struct ahd_linux_device *dev;
+ int was_queuing;
+ int now_queuing;
+
+ dev = ahd_linux_get_device(ahd, devinfo->channel - 'A',
+ devinfo->target,
+ devinfo->lun, /*alloc*/FALSE);
+ if (dev == NULL)
+ return;
+ was_queuing = dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED);
+ now_queuing = alg != AHD_QUEUE_NONE;
+ if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) == 0
+ && (was_queuing != now_queuing)
+ && (dev->active != 0)) {
+ dev->flags |= AHD_DEV_FREEZE_TIL_EMPTY;
+ dev->qfrozen++;
+ }
+
+ dev->flags &= ~(AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED|AHD_DEV_PERIODIC_OTAG);
+ if (now_queuing) {
+ u_int usertags;
+
+ usertags = ahd_linux_user_tagdepth(ahd, devinfo);
+ if (!was_queuing) {
+ /*
+ * Start out agressively and allow our
+ * dynamic queue depth algorithm to take
+ * care of the rest.
+ */
+ dev->maxtags = usertags;
+ dev->openings = dev->maxtags - dev->active;
+ }
+ if (alg == AHD_QUEUE_TAGGED) {
+ dev->flags |= AHD_DEV_Q_TAGGED;
+ if (aic79xx_periodic_otag != 0)
+ dev->flags |= AHD_DEV_PERIODIC_OTAG;
+ } else
+ dev->flags |= AHD_DEV_Q_BASIC;
+ } else {
+ /* We can only have one opening. */
+ dev->maxtags = 0;
+ dev->openings = 1 - dev->active;
+ }
+}
+
+int
+ahd_platform_abort_scbs(struct ahd_softc *ahd, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status)
+{
+ int targ;
+ int maxtarg;
+ int maxlun;
+ int clun;
+ int count;
+
+ if (tag != SCB_LIST_NULL)
+ return (0);
+
+ targ = 0;
+ if (target != CAM_TARGET_WILDCARD) {
+ targ = target;
+ maxtarg = targ + 1;
+ } else {
+ maxtarg = (ahd->features & AHD_WIDE) ? 16 : 8;
+ }
+ clun = 0;
+ if (lun != CAM_LUN_WILDCARD) {
+ clun = lun;
+ maxlun = clun + 1;
+ } else {
+ maxlun = AHD_NUM_LUNS;
+ }
+
+ count = 0;
+ for (; targ < maxtarg; targ++) {
+
+ for (; clun < maxlun; clun++) {
+ struct ahd_linux_device *dev;
+ struct ahd_busyq *busyq;
+ struct ahd_cmd *acmd;
+
+ dev = ahd_linux_get_device(ahd, /*chan*/0, targ,
+ clun, /*alloc*/FALSE);
+ if (dev == NULL)
+ continue;
+
+ busyq = &dev->busyq;
+ while ((acmd = TAILQ_FIRST(busyq)) != NULL) {
+ Scsi_Cmnd *cmd;
+
+ cmd = &acmd_scsi_cmd(acmd);
+ TAILQ_REMOVE(busyq, acmd,
+ acmd_links.tqe);
+ count++;
+ cmd->result = status << 16;
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+ }
+ }
+ }
+
+ return (count);
+}
+
+/*
+ * Sets the queue depth for each SCSI device hanging
+ * off the input host adapter.
+ */
+static void
+ahd_linux_select_queue_depth(struct Scsi_Host * host,
+ Scsi_Device * scsi_devs)
+{
+ Scsi_Device *device;
+ struct ahd_softc *ahd;
+ u_long flags;
+ int scbnum;
+
+ ahd = *((struct ahd_softc **)host->hostdata);
+ ahd_lock(ahd, &flags);
+ scbnum = 0;
+ for (device = scsi_devs; device != NULL; device = device->next) {
+ if (device->host == host) {
+ ahd_linux_device_queue_depth(ahd, device);
+ scbnum += device->queue_depth;
+ }
+ }
+ ahd_unlock(ahd, &flags);
+}
+
+static u_int
+ahd_linux_user_tagdepth(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ static int warned_user;
+ u_int tags;
+
+ tags = 0;
+ if ((ahd->user_discenable & devinfo->target_mask) != 0) {
+ if (warned_user == 0
+ && ahd->unit >= NUM_ELEMENTS(aic79xx_tag_info)) {
+
+ printf("aic79xx: WARNING, insufficient "
+ "tag_info instances for installed "
+ "controllers. Using defaults\n");
+ printf("aic79xx: Please update the "
+ "aic79xx_tag_info array in the "
+ "aic79xx.c source file.\n");
+ tags = AHD_MAX_QUEUE;
+ warned_user++;
+ } else {
+ adapter_tag_info_t *tag_info;
+
+ tag_info = &aic79xx_tag_info[ahd->unit];
+ tags = tag_info->tag_commands[devinfo->target_offset];
+ if (tags > AHD_MAX_QUEUE)
+ tags = AHD_MAX_QUEUE;
+ }
+ }
+ return (tags);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahd_linux_device_queue_depth(struct ahd_softc *ahd, Scsi_Device * device)
+{
+ struct ahd_devinfo devinfo;
+ u_int tags;
+
+ ahd_compile_devinfo(&devinfo,
+ ahd->our_id,
+ device->id, device->lun,
+ device->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+ tags = ahd_linux_user_tagdepth(ahd, &devinfo);
+ if (tags != 0
+ && device->tagged_supported != 0) {
+
+ device->queue_depth = tags;
+ ahd_set_tags(ahd, &devinfo, AHD_QUEUE_TAGGED);
+ printf("scsi%d:%c:%d:%d: Tagged Queuing enabled. Depth %d\n",
+ ahd->platform_data->host->host_no, devinfo.channel,
+ devinfo.target, devinfo.lun, tags);
+ } else {
+ /*
+ * We allow the OS to queue 2 untagged transactions to
+ * us at any time even though we can only execute them
+ * serially on the controller/device. This should remove
+ * some latency.
+ */
+ device->queue_depth = 2;
+ }
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+int
+ahd_linux_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *))
+{
+ struct ahd_softc *ahd;
+ struct ahd_linux_device *dev;
+ u_long flags;
+
+ ahd = *(struct ahd_softc **)cmd->host->hostdata;
+
+ /*
+ * Save the callback on completion function.
+ */
+ cmd->scsi_done = scsi_done;
+
+ ahd_lock(ahd, &flags);
+ dev = ahd_linux_get_device(ahd, cmd->channel, cmd->target,
+ cmd->lun, /*alloc*/TRUE);
+ if (dev == NULL) {
+ ahd_unlock(ahd, &flags);
+ printf("aic79xx_linux_queue: Unable to allocate device!\n");
+ return (-ENOMEM);
+ }
+ if (cmd->cmd_len > MAX_CDB_LEN)
+ return (-EINVAL);
+ cmd->result = CAM_REQ_INPROG << 16;
+ TAILQ_INSERT_TAIL(&dev->busyq, (struct ahd_cmd *)cmd, acmd_links.tqe);
+ if ((dev->flags & AHD_DEV_ON_RUN_LIST) == 0) {
+ TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, dev, links);
+ dev->flags |= AHD_DEV_ON_RUN_LIST;
+ ahd_linux_run_device_queues(ahd);
+ }
+ ahd_unlock(ahd, &flags);
+ return (0);
+}
+
+static void
+ahd_linux_run_device_queue(struct ahd_softc *ahd, struct ahd_linux_device *dev)
+{
+ struct ahd_cmd *acmd;
+ struct scsi_cmnd *cmd;
+ struct scb *scb;
+ struct hardware_scb *hscb;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ uint16_t mask;
+
+ if ((dev->flags & AHD_DEV_ON_RUN_LIST) != 0)
+ panic("running device on run list");
+
+ while ((acmd = TAILQ_FIRST(&dev->busyq)) != NULL
+ && dev->openings > 0 && dev->qfrozen == 0) {
+
+ /*
+ * Schedule us to run later. The only reason we are not
+ * running is because the whole controller Q is frozen.
+ */
+ if (ahd->platform_data->qfrozen != 0) {
+
+ TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq,
+ dev, links);
+ dev->flags |= AHD_DEV_ON_RUN_LIST;
+ return;
+ }
+ /*
+ * Get an scb to use.
+ */
+ if ((scb = ahd_get_scb(ahd)) == NULL) {
+ TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq,
+ dev, links);
+ dev->flags |= AHD_DEV_ON_RUN_LIST;
+ ahd->flags |= AHD_RESOURCE_SHORTAGE;
+ return;
+ }
+ TAILQ_REMOVE(&dev->busyq, acmd, acmd_links.tqe);
+ cmd = &acmd_scsi_cmd(acmd);
+ scb->io_ctx = cmd;
+ scb->platform_data->dev = dev;
+ hscb = scb->hscb;
+ cmd->host_scribble = (char *)scb;
+
+ /*
+ * Fill out basics of the HSCB.
+ */
+ hscb->control = 0;
+ hscb->scsiid = BUILD_SCSIID(ahd, cmd);
+ hscb->lun = cmd->lun;
+ mask = SCB_GET_TARGET_MASK(ahd, scb);
+ tinfo = ahd_fetch_transinfo(ahd, SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_OUR_ID(scb),
+ SCB_GET_TARGET(ahd, scb), &tstate);
+
+ if ((ahd->user_discenable & mask) != 0)
+ hscb->control |= DISCENB;
+
+ if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0)
+ scb->flags |= SCB_PACKETIZED;
+
+ if ((tstate->auto_negotiate & mask) != 0) {
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ scb->hscb->control |= MK_MESSAGE;
+ }
+
+ if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) {
+ if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH
+ && (dev->flags & AHD_DEV_Q_TAGGED) != 0) {
+ hscb->control |= MSG_ORDERED_TASK;
+ dev->commands_since_idle_or_otag = 0;
+ } else {
+ hscb->control |= MSG_SIMPLE_TASK;
+ }
+ }
+
+ hscb->cdb_len = cmd->cmd_len;
+ memcpy(hscb->shared_data.idata.cdb, cmd->cmnd, hscb->cdb_len);
+
+ scb->sg_count = 0;
+ ahd_set_residual(scb, 0);
+ ahd_set_sense_residual(scb, 0);
+ if (cmd->use_sg != 0) {
+ void *sg;
+ struct scatterlist *cur_seg;
+ u_int nseg;
+ int dir;
+
+ cur_seg = (struct scatterlist *)cmd->request_buffer;
+ dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+ nseg = pci_map_sg(ahd->dev_softc, cur_seg,
+ cmd->use_sg, dir);
+ scb->platform_data->xfer_len = 0;
+ for (sg = scb->sg_list; nseg > 0; nseg--, cur_seg++) {
+ bus_addr_t addr;
+ bus_size_t len;
+
+ addr = sg_dma_address(cur_seg);
+ len = sg_dma_len(cur_seg);
+ scb->platform_data->xfer_len += len;
+ sg = ahd_sg_setup(ahd, scb, sg, addr, len,
+ /*last*/nseg == 1);
+ }
+ } else if (cmd->request_bufflen != 0) {
+ void *sg;
+ bus_addr_t addr;
+ int dir;
+
+ sg = scb->sg_list;
+ dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+ addr = pci_map_single(ahd->dev_softc,
+ cmd->request_buffer,
+ cmd->request_bufflen, dir);
+ scb->platform_data->xfer_len = cmd->request_bufflen;
+ scb->platform_data->buf_busaddr = addr;
+ sg = ahd_sg_setup(ahd, scb, sg, addr,
+ cmd->request_bufflen, /*last*/TRUE);
+ }
+
+ LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links);
+ dev->openings--;
+ dev->active++;
+ dev->commands_issued++;
+ if ((dev->flags & AHD_DEV_PERIODIC_OTAG) != 0)
+ dev->commands_since_idle_or_otag++;
+ scb->flags |= SCB_ACTIVE;
+ ahd_queue_scb(ahd, scb);
+ }
+}
+
+/*
+ * SCSI controller interrupt handler.
+ */
+void
+ahd_linux_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct ahd_softc *ahd;
+ struct ahd_cmd *acmd;
+ u_long flags;
+ struct ahd_linux_device *next_dev;
+
+ ahd = (struct ahd_softc *) dev_id;
+ ahd_lock(ahd, &flags);
+ ahd_intr(ahd);
+ acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+ TAILQ_INIT(&ahd->platform_data->completeq);
+ next_dev = ahd_linux_next_device_to_run(ahd);
+ ahd_unlock(ahd, &flags);
+ if (next_dev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_schedule(&ahd->platform_data->runq_tasklet);
+#else
+ ahd_runq_tasklet((unsigned long)ahd);
+#endif
+ }
+ if (acmd != NULL)
+ ahd_linux_run_complete_queue(ahd, acmd);
+}
+
+void
+ahd_platform_flushwork(struct ahd_softc *ahd)
+{
+ struct ahd_cmd *acmd;
+
+ acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+ TAILQ_INIT(&ahd->platform_data->completeq);
+ if (acmd != NULL)
+ ahd_linux_run_complete_queue(ahd, acmd);
+}
+
+static struct ahd_linux_target*
+ahd_linux_alloc_target(struct ahd_softc *ahd, u_int channel, u_int target)
+{
+ struct ahd_linux_target *targ;
+ u_int target_offset;
+
+ targ = malloc(sizeof(*targ), M_DEVBUG, M_NOWAIT);
+ if (targ == NULL)
+ return (NULL);
+ memset(targ, 0, sizeof(*targ));
+ targ->channel = channel;
+ targ->target = target;
+ targ->ahd = ahd;
+ target_offset = target;
+ if (channel != 0)
+ target_offset += 8;
+ ahd->platform_data->targets[target_offset] = targ;
+ return (targ);
+}
+
+static void
+ahd_linux_free_target(struct ahd_softc *ahd, struct ahd_linux_target *targ)
+{
+ u_int target_offset;
+
+ target_offset = targ->target;
+ if (targ->channel != 0)
+ target_offset += 8;
+ ahd->platform_data->targets[target_offset] = NULL;
+ free(targ, M_DEVBUF);
+}
+
+static struct ahd_linux_device*
+ahd_linux_alloc_device(struct ahd_softc *ahd,
+ struct ahd_linux_target *targ, u_int lun)
+{
+ struct ahd_linux_device *dev;
+
+ dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT);
+ if (dev == NULL)
+ return (NULL);
+ memset(dev, 0, sizeof(*dev));
+ init_timer(&dev->timer);
+ TAILQ_INIT(&dev->busyq);
+ dev->flags = AHD_DEV_UNCONFIGURED;
+ dev->lun = lun;
+ dev->target = targ;
+
+ /*
+ * We start out life using untagged
+ * transactions of which we allow one.
+ */
+ dev->openings = 1;
+
+ /*
+ * Set maxtags to 0. This will be changed if we
+ * later determine that we are dealing with
+ * a tagged queuing capable device.
+ */
+ dev->maxtags = 0;
+
+ targ->refcount++;
+ targ->devices[lun] = dev;
+ return (dev);
+}
+
+static void
+ahd_linux_free_device(struct ahd_softc *ahd, struct ahd_linux_device *dev)
+{
+ struct ahd_linux_target *targ;
+
+ del_timer(&dev->timer);
+ targ = dev->target;
+ targ->devices[dev->lun] = NULL;
+ free(dev, M_DEVBUF);
+ targ->refcount--;
+ if (targ->refcount == 0)
+ ahd_linux_free_target(ahd, targ);
+}
+
+/*
+ * Return a string describing the driver.
+ */
+const char *
+ahd_linux_info(struct Scsi_Host *host)
+{
+ static char buffer[512];
+ char ahd_info[256];
+ char *bp;
+ struct ahd_softc *ahd;
+
+ bp = &buffer[0];
+ ahd = *(struct ahd_softc **)host->hostdata;
+ memset(bp, 0, sizeof(buffer));
+ strcpy(bp, "Adaptec AIC79XX PCI-X SCSI HBA DRIVER, Rev ");
+ strcat(bp, AIC79XX_DRIVER_VERSION);
+ strcat(bp, "\n");
+ strcat(bp, " <");
+ strcat(bp, ahd->description);
+ strcat(bp, ">\n");
+ strcat(bp, " ");
+ ahd_controller_info(ahd, ahd_info);
+ strcat(bp, ahd_info);
+ strcat(bp, "\n");
+
+ return (bp);
+}
+
+void
+ahd_send_async(struct ahd_softc *ahd, char channel,
+ u_int target, u_int lun, ac_code code, void *arg)
+{
+ switch (code) {
+ case AC_TRANSFER_NEG:
+ {
+ char buf[80];
+ struct ahd_linux_target *targ;
+ struct info_str info;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ info.buffer = buf;
+ info.length = sizeof(buf);
+ info.offset = 0;
+ info.pos = 0;
+ tinfo = ahd_fetch_transinfo(ahd, channel, ahd->our_id,
+ target, &tstate);
+
+ /*
+ * Don't bother reporting results while
+ * negotiations are still pending.
+ */
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options)
+ if (bootverbose == 0)
+ break;
+
+ /*
+ * Don't bother reporting results that
+ * are identical to those last reported.
+ */
+ targ = ahd->platform_data->targets[target];
+ if (targ == NULL)
+ break;
+ if (tinfo->curr.period == targ->last_tinfo.period
+ && tinfo->curr.width == targ->last_tinfo.width
+ && tinfo->curr.offset == targ->last_tinfo.offset
+ && tinfo->curr.ppr_options == targ->last_tinfo.ppr_options)
+ if (bootverbose == 0)
+ break;
+
+ targ->last_tinfo.period = tinfo->curr.period;
+ targ->last_tinfo.width = tinfo->curr.width;
+ targ->last_tinfo.offset = tinfo->curr.offset;
+ targ->last_tinfo.ppr_options = tinfo->curr.ppr_options;
+
+ printf("(%s:%c:", ahd_name(ahd), channel);
+ if (target == CAM_TARGET_WILDCARD)
+ printf("*): ");
+ else
+ printf("%d): ", target);
+ ahd_format_transinfo(&info, &tinfo->curr);
+ if (info.pos < info.length)
+ *info.buffer = '\0';
+ else
+ buf[info.length - 1] = '\0';
+ printf("%s", buf);
+ break;
+ }
+ case AC_SENT_BDR:
+ break;
+ case AC_BUS_RESET:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ if (ahd->platform_data->host != NULL) {
+ scsi_report_bus_reset(ahd->platform_data->host,
+ channel - 'A');
+ }
+#endif
+ break;
+ default:
+ panic("ahd_send_async: Unexpected async event");
+ }
+}
+
+/*
+ * Calls the higher level scsi done function and frees the scb.
+ */
+void
+ahd_done(struct ahd_softc *ahd, struct scb * scb)
+{
+ Scsi_Cmnd *cmd;
+ struct ahd_linux_device *dev;
+
+ LIST_REMOVE(scb, pending_links);
+ if ((scb->flags & SCB_UNTAGGEDQ) != 0) {
+ struct scb_tailq *untagged_q;
+ int target_offset;
+
+ target_offset = SCB_GET_TARGET_OFFSET(ahd, scb);
+ untagged_q = &(ahd->untagged_queues[target_offset]);
+ TAILQ_REMOVE(untagged_q, scb, links.tqe);
+ ahd_run_untagged_queue(ahd, untagged_q);
+ }
+
+ if ((scb->flags & SCB_ACTIVE) == 0) {
+ printf("SCB %d done'd twice\n", scb->hscb->tag);
+ ahd_dump_card_state(ahd);
+ panic("Stopping for safety");
+ }
+ cmd = scb->io_ctx;
+ dev = scb->platform_data->dev;
+ dev->active--;
+ dev->openings++;
+ ahd_linux_unmap_scb(ahd, scb);
+ if (scb->flags & SCB_SENSE) {
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ memcpy(cmd->sense_buffer, ahd_get_sense_buf(ahd, scb),
+ MIN(sizeof(struct scsi_sense_data),
+ sizeof(cmd->sense_buffer)));
+ cmd->result |= (DRIVER_SENSE << 24);
+ } else if (scb->flags & SCB_PKT_SENSE) {
+ struct scsi_status_iu_header *siu;
+ u_int sense_len;
+
+ /*
+ * Copy only the sense data into the provided buffer.
+ */
+ siu = (struct scsi_status_iu_header *)scb->sense_data;
+ sense_len = MIN(scsi_4btoul(siu->sense_length),
+ sizeof(cmd->sense_buffer));
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ memcpy(cmd->sense_buffer,
+ ahd_get_sense_buf(ahd, scb) + SIU_SENSE_OFFSET(siu),
+ sense_len);
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_SENSE) {
+ int i;
+
+ printf("Copied %d bytes of sense data offset %d:",
+ sense_len, SIU_SENSE_OFFSET(siu));
+ for (i = 0; i < sense_len; i++)
+ printf(" 0x%x", cmd->sense_buffer[i]);
+ printf("\n");
+ }
+#endif
+ cmd->result |= (DRIVER_SENSE << 24);
+ } else {
+ /*
+ * Guard against stale sense data.
+ * The Linux mid-layer assumes that sense
+ * was retrieved anytime the first byte of
+ * the sense buffer looks "sane".
+ */
+ cmd->sense_buffer[0] = 0;
+ }
+ if (ahd_get_transaction_status(scb) == CAM_REQ_INPROG) {
+ uint32_t amount_xferred;
+
+ amount_xferred =
+ ahd_get_transfer_length(scb) - ahd_get_residual(scb);
+ if (amount_xferred < scb->io_ctx->underflow) {
+ printf("Saw underflow (%ld of %ld bytes). "
+ "Treated as error\n",
+ ahd_get_residual(scb),
+ ahd_get_transfer_length(scb));
+ ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ } else {
+ ahd_set_transaction_status(scb, CAM_REQ_CMP);
+ ahd_linux_sniff_command(ahd, cmd, scb);
+ }
+ } else if (ahd_get_transaction_status(scb) == DID_OK) {
+ ahd_linux_handle_scsi_status(ahd, dev, scb);
+ } else if (ahd_get_transaction_status(scb) == DID_NO_CONNECT) {
+ /*
+ * Should a selection timeout kill the device?
+ * That depends on whether the selection timeout
+ * is persistent. Since we have no guarantee that
+ * the mid-layer will issue an inquiry for this device
+ * again, we can't just kill it off.
+ dev->flags |= AHD_DEV_UNCONFIGURED;
+ */
+ }
+
+ if (dev->openings == 1
+ && ahd_get_transaction_status(scb) == CAM_REQ_CMP
+ && ahd_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+ dev->tag_success_count++;
+ /*
+ * Some devices deal with temporary internal resource
+ * shortages by returning queue full. When the queue
+ * full occurrs, we throttle back. Slowly try to get
+ * back to our previous queue depth.
+ */
+ if ((dev->openings + dev->active) < dev->maxtags
+ && dev->tag_success_count > AHD_TAG_SUCCESS_INTERVAL) {
+ dev->tag_success_count = 0;
+ dev->openings++;
+ }
+
+ if (dev->active == 0)
+ dev->commands_since_idle_or_otag = 0;
+
+ if (TAILQ_EMPTY(&dev->busyq)) {
+ if ((dev->flags & AHD_DEV_UNCONFIGURED) != 0
+ && dev->active == 0)
+ ahd_linux_free_device(ahd, dev);
+ } else if ((dev->flags & AHD_DEV_ON_RUN_LIST) == 0) {
+ TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, dev, links);
+ dev->flags |= AHD_DEV_ON_RUN_LIST;
+ }
+
+ if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+ printf("Recovery SCB completes\n");
+ up(&ahd->platform_data->eh_sem);
+ }
+
+ ahd_free_scb(ahd, scb);
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+}
+
+static void
+ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev, struct scb *scb)
+{
+ /*
+ * We don't currently trust the mid-layer to
+ * properly deal with queue full or busy. So,
+ * when one occurs, we tell the mid-layer to
+ * unconditionally requeue the command to us
+ * so that we can retry it ourselves. We also
+ * implement our own throttling mechanism so
+ * we don't clobber the device with too many
+ * commands.
+ */
+ switch (ahd_get_scsi_status(scb)) {
+ default:
+ break;
+ case SCSI_STATUS_QUEUE_FULL:
+ {
+ /*
+ * By the time the core driver has returned this
+ * command, all other commands that were queued
+ * to us but not the device have been returned.
+ * This ensures that dev->active is equal to
+ * the number of commands actually queued to
+ * the device.
+ */
+ dev->tag_success_count = 0;
+ if (dev->active != 0) {
+ /*
+ * Drop our opening count to the number
+ * of commands currently outstanding.
+ */
+ dev->openings = 0;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_QFULL) {
+ ahd_print_path(ahd, scb);
+ printf("Dropping tag count to %d\n",
+ dev->active);
+ }
+#endif
+ if (dev->active == dev->tags_on_last_queuefull) {
+
+ dev->last_queuefull_same_count++;
+ /*
+ * If we repeatedly see a queue full
+ * at the same queue depth, this
+ * device has a fixed number of tag
+ * slots. Lock in this tag depth
+ * so we stop seeing queue fulls from
+ * this device.
+ */
+ if (dev->last_queuefull_same_count
+ == AHD_LOCK_TAGS_COUNT) {
+ dev->maxtags = dev->active;
+ ahd_print_path(ahd, scb);
+ printf("Locking max tag count at %d\n",
+ dev->active);
+ }
+ } else {
+ dev->tags_on_last_queuefull = dev->active;
+ dev->last_queuefull_same_count = 0;
+ }
+ ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+ ahd_set_scsi_status(scb, SCSI_STATUS_OK);
+ break;
+ }
+ /*
+ * Drop down to a single opening, and treat this
+ * as if the target return BUSY SCSI status.
+ */
+ dev->openings = 1;
+ /* FALLTHROUGH */
+ }
+ case SCSI_STATUS_BUSY:
+ /*
+ * Set a short timer to defer sending commands for
+ * a bit since Linux will not delay in this case.
+ */
+ if ((dev->flags & AHD_DEV_TIMER_ACTIVE) != 0) {
+ printf("%s:%c:%d: Device Timer still active during "
+ "busy processing\n", ahd_name(ahd),
+ dev->target->channel, dev->target->target);
+ break;
+ }
+ dev->flags |= AHD_DEV_TIMER_ACTIVE;
+ dev->qfrozen++;
+ init_timer(&dev->timer);
+ dev->timer.data = (u_long)dev;
+ dev->timer.expires = jiffies + (HZ/2);
+ dev->timer.function = ahd_linux_dev_timed_unfreeze;
+ add_timer(&dev->timer);
+ break;
+ }
+}
+
+static void
+ahd_linux_filter_command(struct ahd_softc *ahd, Scsi_Cmnd *cmd, struct scb *scb)
+{
+ switch (cmd->cmnd[0]) {
+ case INQUIRY:
+ {
+ struct ahd_devinfo devinfo;
+ struct scsi_inquiry *inq;
+ struct scsi_inquiry_data *sid;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_transinfo *user;
+ struct ahd_transinfo *goal;
+ struct ahd_transinfo *curr;
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_linux_device *dev;
+ u_int scsiid;
+ int transferred_len;
+ int minlen;
+ int was_configured;
+ u_int width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+ u_int trans_version;
+ u_int prot_version;
+ static int warned_user;
+
+ /*
+ * Validate the command. We only want to filter
+ * standard inquiry commands, not those querying
+ * Vital Product Data.
+ */
+ inq = (struct scsi_inquiry *)cmd->cmnd;
+ if ((inq->byte2 & SI_EVPD) != 0
+ || inq->page_code != 0)
+ break;
+
+ if (cmd->use_sg != 0) {
+ printf("%s: SG Inquiry response ignored\n",
+ ahd_name(ahd));
+ break;
+ }
+ transferred_len = ahd_get_transfer_length(scb)
+ - ahd_get_residual(scb);
+ sid = (struct scsi_inquiry_data *)cmd->request_buffer;
+
+ /*
+ * Determine if this lun actually exists. If so,
+ * hold on to its corresponding device structure.
+ * If not, make sure we release the device and
+ * don't bother processing the rest of this inquiry
+ * command.
+ */
+ dev = ahd_linux_get_device(ahd, cmd->channel,
+ cmd->target, cmd->lun,
+ /*alloc*/FALSE);
+ was_configured = dev->flags & AHD_DEV_UNCONFIGURED;
+ if (transferred_len >= 1
+ && SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
+
+ dev->flags &= ~AHD_DEV_UNCONFIGURED;
+ } else {
+ dev->flags |= AHD_DEV_UNCONFIGURED;
+ break;
+ }
+
+ /*
+ * Update our notion of this device's transfer
+ * negotiation capabilities.
+ */
+ scsiid = BUILD_SCSIID(ahd, cmd);
+ ahd_compile_devinfo(&devinfo, SCSIID_OUR_ID(scsiid),
+ cmd->target, cmd->lun,
+ SCSIID_CHANNEL(ahd, scsiid),
+ ROLE_INITIATOR);
+ tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ user = &tinfo->user;
+ goal = &tinfo->goal;
+ curr = &tinfo->curr;
+ width = user->width;
+ period = user->period;
+ offset = user->offset;
+ ppr_options = user->ppr_options;
+ trans_version = user->transport_version;
+ prot_version = user->protocol_version;
+ /*
+ * If we have read streaming info for this controller,
+ * apply it to this target.
+ */
+ if (warned_user == 0
+ && ahd->unit >= NUM_ELEMENTS(aic79xx_rd_strm_info)) {
+
+ printf("aic79xx: WARNING, insufficient "
+ "rd_strm instances for installed "
+ "controllers. Using defaults\n");
+ printf("aic79xx: Please update the "
+ "aic79xx_rd_strm_info array in the "
+ "aic79xx.c source file.\n");
+ warned_user++;
+ } else {
+ uint16_t rd_strm_mask;
+
+ rd_strm_mask = aic79xx_rd_strm_info[ahd->unit];
+ if ((rd_strm_mask & devinfo.target_mask) == 0)
+ ppr_options &= ~MSG_EXT_PPR_RD_STRM;
+ }
+
+ minlen = offsetof(struct scsi_inquiry_data, version) + 1;
+ if (transferred_len >= minlen) {
+ prot_version = SID_ANSI_REV(sid);
+
+ /*
+ * Only attempt SPI3 once we've verified that
+ * the device claims to support SPI3 features.
+ */
+ if (prot_version < SCSI_REV_2)
+ trans_version = SID_ANSI_REV(sid);
+ else
+ trans_version = SCSI_REV_2;
+ }
+
+ minlen = offsetof(struct scsi_inquiry_data, flags) + 1;
+ if (transferred_len >= minlen
+ && (sid->additional_length + 4) >= minlen) {
+ if ((sid->flags & SID_WBus16) == 0)
+ width = MSG_EXT_WDTR_BUS_8_BIT;
+ if ((sid->flags & SID_Sync) == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ }
+ } else {
+ /* Keep current settings */
+ break;
+ }
+ minlen = offsetof(struct scsi_inquiry_data, spi3data) + 1;
+ /*
+ * This is a kludge to deal with inquiry requests that
+ * are not large enough for us to pull the spi3/4 bits.
+ * In this case, we assume that a device that tells us
+ * they can provide inquiry data that spans the SPI3
+ * bits and says its SCSI3 can handle a PPR request.
+ * If the inquiry request has sufficient buffer space to
+ * cover SPI3 bits, we honor them regardless of reported
+ * SCSI REV. We also allow any device that has had its
+ * goal ppr_options set to allow DT speeds to keep that
+ * option if a short inquiry occurs that would fail the
+ * normal tests outlined above.
+ */
+ if ((sid->additional_length + 4) >= minlen) {
+ if (transferred_len >= minlen) {
+ if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0)
+ ppr_options = 0;
+ if ((sid->spi3data & SID_SPI_IUS) == 0)
+ ppr_options &= (MSG_EXT_PPR_DT_REQ
+ | MSG_EXT_PPR_QAS_REQ);
+ } else if (was_configured
+ && (curr->transport_version
+ == user->transport_version)) {
+
+ /* Keep already existant settings. */
+ break;
+ } else if ((goal->ppr_options & MSG_EXT_PPR_DT_REQ)== 0)
+ ppr_options = 0;
+
+ if (curr->protocol_version > SCSI_REV_2)
+ trans_version = user->transport_version;
+ } else {
+ ppr_options = 0;
+ }
+ ahd_validate_width(ahd, /*tinfo limit*/NULL, &width,
+ ROLE_UNKNOWN);
+
+ ahd_find_syncrate(ahd, &period, &ppr_options, AHD_SYNCRATE_MAX);
+ ahd_validate_offset(ahd, /*tinfo limit*/NULL, period,
+ &offset, width, ROLE_UNKNOWN);
+ if (offset == 0 || period == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ }
+ /* Apply our filtered user settings. */
+ curr->transport_version = trans_version;
+ curr->protocol_version = prot_version;
+ ahd_set_width(ahd, &devinfo, width,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_set_syncrate(ahd, &devinfo, period, offset, ppr_options,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ break;
+ }
+ default:
+ panic("ahd_linux_filter_command: Unexpected Command type %x\n",
+ cmd->cmnd[0]);
+ break;
+ }
+}
+
+void
+ahd_freeze_simq(struct ahd_softc *ahd)
+{
+ ahd->platform_data->qfrozen++;
+ if (ahd->platform_data->qfrozen == 1)
+ scsi_block_requests(ahd->platform_data->host);
+}
+
+void
+ahd_release_simq(struct ahd_softc *ahd)
+{
+ u_long s;
+ int unblock_reqs;
+
+ unblock_reqs = 0;
+ ahd_lock(ahd, &s);
+ if (ahd->platform_data->qfrozen > 0)
+ ahd->platform_data->qfrozen--;
+ if (ahd->platform_data->qfrozen == 0) {
+ unblock_reqs = 1;
+ }
+ ahd_unlock(ahd, &s);
+ /*
+ * There is still a race here. The mid-layer
+ * should keep its own freeze count and use
+ * a bottom half handler to run the queues
+ * so we can unblock with our own lock held.
+ */
+ if (unblock_reqs) {
+ scsi_unblock_requests(ahd->platform_data->host);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_schedule(&ahd->platform_data->runq_tasklet);
+#else
+ ahd_runq_tasklet((unsigned long)ahd);
+#endif
+ }
+}
+
+#if NOT_YET
+static void
+ahd_linux_sem_timeout(u_long arg)
+{
+ struct semaphore *sem;
+
+ sem = (struct semaphore *)arg;
+ up(sem);
+}
+
+static int
+ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
+{
+ struct ahd_softc *ahd;
+ struct ahd_cmd *acmd;
+ struct ahd_cmd *list_acmd;
+ struct ahd_linux_device *dev;
+ struct scb *pending_scb;
+ u_long s;
+ u_int saved_scbptr;
+ u_int active_scb_index;
+ u_int last_phase;
+ int retval;
+ int paused;
+ int wait;
+ int disconnected;
+
+ paused = FALSE;
+ wait = FALSE;
+ ahd = *(struct ahd_softc **)cmd->host->hostdata;
+ acmd = (struct ahd_cmd *)cmd;
+
+ printf("%s:%d:%d:%d: Attempting to queue a%s message\n",
+ ahd_name(ahd), cmd->channel, cmd->target, cmd->lun,
+ flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");
+
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+
+ ahd_lock(ahd, &s);
+
+ /*
+ * First determine if we currently own this command.
+ * Start by searching the device queue. If not found
+ * there, check the pending_scb list. If not found
+ * at all, and the system wanted us to just abort the
+ * command return success.
+ */
+ dev = ahd_linux_get_device(ahd, cmd->channel, cmd->target,
+ cmd->lun, /*alloc*/FALSE);
+
+ if (dev == NULL) {
+ /*
+ * No target device for this command exists,
+ * so we must not still own the command.
+ */
+ printf("%s:%d:%d:%d: Is not an active device\n",
+ ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+ retval = SUCCESS;
+ goto no_cmd;
+ }
+
+ TAILQ_FOREACH(list_acmd, &dev->busyq, acmd_links.tqe) {
+ if (list_acmd == acmd)
+ break;
+ }
+
+ if (list_acmd != NULL) {
+ printf("%s:%d:%d:%d: Command found on device queue\n",
+ ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+ if (flag == SCB_ABORT) {
+ TAILQ_REMOVE(&dev->busyq, list_acmd, acmd_links.tqe);
+ cmd->result = DID_ABORT << 16;
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+ retval = SUCCESS;
+ goto done;
+ }
+ }
+
+ /*
+ * See if we can find a matching cmd in the pending list.
+ */
+ LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+ if (pending_scb->io_ctx == cmd)
+ break;
+ }
+
+ if (pending_scb == NULL && flag == SCB_DEVICE_RESET) {
+
+ /* Any SCB for this device will do for a target reset */
+ LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+ if (ahd_match_scb(ahd, pending_scb, cmd->target,
+ cmd->channel, CAM_LUN_WILDCARD,
+ SCB_LIST_NULL, ROLE_INITIATOR) == 0)
+ break;
+ }
+ }
+
+ if (pending_scb == NULL) {
+ printf("%s:%d:%d:%d: Command not found\n",
+ ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+ goto no_cmd;
+ }
+
+ if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
+ /*
+ * We can't queue two recovery actions using the same SCB
+ */
+ retval = FAILED;
+ goto done;
+ }
+
+ /*
+ * Ensure that the card doesn't do anything
+ * behind our back. Also make sure that we
+ * didn't "just" miss an interrupt that would
+ * affect this cmd.
+ */
+ ahd->flags |= AHD_ALL_INTERRUPTS;
+ do {
+ ahd_intr(ahd);
+ ahd_pause(ahd);
+ ahd_clear_critical_section(ahd);
+ } while (ahd_inb(ahd, INTSTAT) & INT_PEND);
+ ahd->flags &= ~AHD_ALL_INTERRUPTS;
+ paused = TRUE;
+
+ ahd_dump_card_state(ahd);
+
+ if ((pending_scb->flags & SCB_ACTIVE) == 0) {
+ printf("%s:%d:%d:%d: Command already completed\n",
+ ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+ goto no_cmd;
+ }
+
+ disconnected = TRUE;
+ if (flag == SCB_ABORT) {
+ if (ahd_search_qinfifo(ahd, cmd->target, cmd->channel + 'A',
+ cmd->lun, pending_scb->hscb->tag,
+ ROLE_INITIATOR, CAM_REQ_ABORTED,
+ SEARCH_COMPLETE) > 0) {
+ printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
+ ahd_name(ahd), cmd->channel, cmd->target,
+ cmd->lun);
+ retval = SUCCESS;
+ goto done;
+ }
+ } else if (ahd_search_qinfifo(ahd, cmd->target, cmd->channel + 'A',
+ cmd->lun, pending_scb->hscb->tag,
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_COUNT) > 0) {
+ disconnected = FALSE;
+ }
+
+ /*
+ * At this point, pending_scb is the scb associated with the
+ * passed in command. That command is currently active on the
+ * bus, is in the disconnected state, or we're hoping to find
+ * a command for the same target active on the bus to abuse to
+ * send a BDR. Queue the appropriate message based on which of
+ * these states we are in.
+ */
+ last_phase = ahd_inb(ahd, LASTPHASE);
+ saved_scbptr = ahd_inb(ahd, SCBPTR);
+ active_scb_index = ahd_inb(ahd, SCB_TAG);
+ if (last_phase != P_BUSFREE
+ && (pending_scb->hscb->tag == active_scb_index
+ || (flag == SCB_DEVICE_RESET
+ && SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)) == cmd->target))) {
+
+ /*
+ * We're active on the bus, so assert ATN
+ * and hope that the target responds.
+ */
+ pending_scb = ahd_lookup_scb(ahd, active_scb_index);
+ pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd_outb(ahd, SCSISIGO, last_phase|ATNO);
+ printf("%s:%d:%d:%d: Device is active, asserting ATN\n",
+ ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+ wait = TRUE;
+ } else if (disconnected) {
+
+ /*
+ * Actually re-queue this SCB in an attempt
+ * to select the device before it reconnects.
+ * In either case (selection or reselection),
+ * we will now issue the approprate message
+ * to the timed-out device.
+ *
+ * Set the MK_MESSAGE control bit indicating
+ * that we desire to send a message. We
+ * also set the disconnected flag since
+ * in the paging case there is no guarantee
+ * that our SCB control byte matches the
+ * version on the card. We don't want the
+ * sequencer to abort the command thinking
+ * an unsolicited reselection occurred.
+ */
+ pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+ pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+
+ /*
+ * In the non-paging case, the sequencer will
+ * never re-reference the in-core SCB.
+ * To make sure we are notified during
+ * reslection, set the MK_MESSAGE flag in
+ * the card's copy of the SCB.
+ */
+ ahd_outb(ahd, SCBPTR, pending_scb->hscb->tag);
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE);
+
+ /*
+ * Clear out any entries in the QINFIFO first
+ * so we are the next SCB for this target
+ * to run.
+ */
+ ahd_search_qinfifo(ahd, cmd->target, cmd->channel + 'A',
+ cmd->lun, SCB_LIST_NULL, ROLE_INITIATOR,
+ CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+ ahd_print_path(ahd, pending_scb);
+ printf("Queuing a recovery SCB\n");
+ ahd_qinfifo_requeue_tail(ahd, pending_scb);
+ ahd_outb(ahd, SCBPTR, saved_scbptr);
+ printf("%s:%d:%d:%d: Device is disconnected, re-queuing SCB\n",
+ ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+ wait = TRUE;
+ } else {
+ printf("%s:%d:%d:%d: Unable to deliver message\n",
+ ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+ retval = FAILED;
+ goto done;
+ }
+
+no_cmd:
+ /*
+ * Our assumption is that if we don't have the command, no
+ * recovery action was required, so we return success. Again,
+ * the semantics of the mid-layer recovery engine are not
+ * well defined, so this may change in time.
+ */
+ retval = SUCCESS;
+done:
+ if (paused)
+ ahd_unpause(ahd);
+ if (wait) {
+ struct timer_list timer;
+ int ret;
+
+ ahd_unlock(ahd, &s);
+ init_timer(&timer);
+ timer.data = (u_long)&ahd->platform_data->eh_sem;
+ timer.expires = jiffies + (5 * HZ);
+ timer.function = ahd_linux_sem_timeout;
+ add_timer(&timer);
+ printf("Recovery code sleeping\n");
+ down(&ahd->platform_data->eh_sem);
+ printf("Recovery code awake\n");
+ ret = del_timer(&timer);
+ if (ret == 0) {
+ printf("Timer Expired\n");
+ retval = FAILED;
+ }
+ ahd_lock(ahd, &s);
+ }
+ acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+ TAILQ_INIT(&ahd->platform_data->completeq);
+ ahd_unlock(ahd, &s);
+ if (acmd != NULL)
+ ahd_linux_run_complete_queue(ahd, acmd);
+ ahd_runq_tasklet((unsigned long)ahd);
+ spin_lock_irq(&io_request_lock);
+ return (retval);
+}
+#endif
+
+static void
+ahd_linux_dev_timed_unfreeze(u_long arg)
+{
+ struct ahd_linux_device *dev;
+ struct ahd_softc *ahd;
+ u_long s;
+
+ dev = (struct ahd_linux_device *)arg;
+ ahd = dev->target->ahd;
+ ahd_lock(ahd, &s);
+ dev->flags &= ~AHD_DEV_TIMER_ACTIVE;
+ if (dev->qfrozen > 0)
+ dev->qfrozen--;
+ if (dev->qfrozen == 0
+ && (dev->flags & AHD_DEV_ON_RUN_LIST) == 0)
+ ahd_linux_run_device_queue(ahd, dev);
+ ahd_unlock(ahd, &s);
+}
+
+/*
+ * Abort the current SCSI command(s).
+ */
+int
+ahd_linux_abort(Scsi_Cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+ u_long s;
+#if NOTYET
+ struct ahd_cmd *acmd;
+ int found;
+#endif
+
+ ahd = *(struct ahd_softc **)cmd->host->hostdata;
+#if NOTYET
+ int error;
+
+ error = ahd_linux_queue_recovery_cmd(cmd, SCB_ABORT);
+ if (error != 0)
+ printf("aic79xx_abort returns 0x%x\n", error);
+ return (error);
+#else
+ printf("Abort called for cmd %p\n", cmd);
+ ahd_lock(ahd, &s);
+ ahd_dump_card_state(ahd);
+ ahd_unlock(ahd, &s);
+ return (FAILED);
+#endif
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+int
+ahd_linux_dev_reset(Scsi_Cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+#if NOTYET
+ struct ahd_cmd *acmd;
+ u_long s;
+ int found;
+#endif
+
+ printf("dev reset called for cmd %p\n", cmd);
+ ahd = *(struct ahd_softc **)cmd->host->hostdata;
+#if NOTYET
+ int error;
+
+ error = ahd_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET);
+ if (error != 0)
+ printf("aic79xx_dev_reset returns 0x%x\n", error);
+ return (error);
+#else
+ return (FAILED);
+#endif
+}
+
+/*
+ * Reset the SCSI bus.
+ */
+int
+ahd_linux_bus_reset(Scsi_Cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+ struct ahd_cmd *acmd;
+ u_long s;
+ int found;
+
+ printf("bus reset called for cmd %p\n", cmd);
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+
+ ahd = *(struct ahd_softc **)cmd->host->hostdata;
+ ahd_lock(ahd, &s);
+ found = ahd_reset_channel(ahd, cmd->channel + 'A',
+ /*initiate reset*/TRUE);
+ acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+ TAILQ_INIT(&ahd->platform_data->completeq);
+ ahd_unlock(ahd, &s);
+ if (bootverbose)
+ printf("%s: SCSI bus reset delivered. "
+ "%d SCBs aborted.\n", ahd_name(ahd), found);
+
+ if (acmd != NULL)
+ ahd_linux_run_complete_queue(ahd, acmd);
+
+ spin_lock_irq(&io_request_lock);
+ return (SUCCESS);
+}
+
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+int
+ahd_linux_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+ int heads;
+ int sectors;
+ int cylinders;
+ int ret;
+ int extended;
+ struct ahd_softc *ahd;
+ struct buffer_head *bh;
+
+ ahd = *((struct ahd_softc **)disk->device->host->hostdata);
+ bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024);
+
+ if (bh) {
+ ret = scsi_partsize(bh, disk->capacity,
+ &geom[2], &geom[0], &geom[1]);
+ brelse(bh);
+ if (ret != -1)
+ return (ret);
+ }
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ if (aic79xx_extended != 0)
+ extended = 1;
+ else
+ extended = (ahd->flags & AHD_EXTENDED_TRANS_A) != 0;
+ if (extended && cylinders >= 1024) {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (heads * sectors);
+ }
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ return (0);
+}
+
+/*
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ */
+int
+ahd_linux_release(struct Scsi_Host * host)
+{
+ struct ahd_softc *ahd;
+ u_long l;
+
+ ahd_list_lock(&l);
+ if (host != NULL) {
+
+ /*
+ * We should be able to just perform
+ * the free directly, but check our
+ * list for extra sanity.
+ */
+ ahd = ahd_find_softc(*(struct ahd_softc **)host->hostdata);
+ if (ahd != NULL) {
+ u_long s;
+
+ ahd_lock(ahd, &s);
+ ahd_intr_enable(ahd, FALSE);
+ ahd_unlock(ahd, &s);
+ ahd_free(ahd);
+ }
+ }
+ ahd_list_unlock(&l);
+ return (0);
+}
+
+void
+ahd_platform_dump_card_state(struct ahd_softc *ahd)
+{
+ struct ahd_linux_device *dev;
+ int target;
+ int maxtarget;
+ int lun;
+ int i;
+
+ maxtarget = (ahd->features & AHD_WIDE) ? 15 : 7;
+ for (target = 0; target <=maxtarget; target++) {
+
+ for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+ struct ahd_cmd *acmd;
+
+ dev = ahd_linux_get_device(ahd, 0, target,
+ lun, /*alloc*/FALSE);
+ if (dev == NULL)
+ continue;
+
+ printf("DevQ(%d:%d:%d): ", 0, target, lun);
+ i = 0;
+ TAILQ_FOREACH(acmd, &dev->busyq, acmd_links.tqe) {
+ if (i++ > AHD_SCB_MAX)
+ break;
+ }
+ printf("%d waiting\n", i);
+ }
+ }
+}
+
+#if defined(MODULE) || LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static Scsi_Host_Template driver_template = AIC79XX;
+Scsi_Host_Template *aic79xx_driver_template = &driver_template;
+#include "../scsi_module.c"
+#endif
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_osm.h b/xen/drivers/scsi/aic7xxx/aic79xx_osm.h
new file mode 100644
index 0000000000..1c81a586c3
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_osm.h
@@ -0,0 +1,1272 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.h#35 $
+ *
+ */
+#ifndef _AIC79XX_LINUX_H_
+#define _AIC79XX_LINUX_H_
+
+#include <linux/types.h>
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+#ifndef AHD_MODVERSION_FILE
+#define __NO_VERSION__
+#endif
+#include <linux/module.h>
+#include <asm/byteorder.h>
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#include <linux/interrupt.h> /* For tasklet support. */
+#include <linux/config.h>
+#include <linux/slab.h>
+#else
+#include <linux/malloc.h>
+#endif
+
+/* Core SCSI definitions */
+#include "../scsi.h"
+#include "../hosts.h"
+
+/* Name space conflict with BSD queue macros */
+#ifdef LIST_HEAD
+#undef LIST_HEAD
+#endif
+
+#include "cam.h"
+#include "queue.h"
+#include "scsi_message.h"
+#include "scsi_iu.h"
+
+/*********************************** Debugging ********************************/
+#ifdef CONFIG_AIC79XX_DEBUG_ENABLE
+#ifdef CONFIG_AIC79XX_DEBUG_MASK
+#define AHD_DEBUG CONFIG_AIC79XX_DEBUG_MASK
+#else
+/*
+ * Compile in debugging code, but do not enable any printfs.
+ */
+#define AHD_DEBUG 0
+#endif
+#endif
+
+/********************************** Misc Macros *******************************/
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+#define powerof2(x) ((((x)-1)&(x))==0)
+
+/************************* Forward Declarations *******************************/
+struct ahd_softc;
+typedef struct pci_dev *ahd_dev_softc_t;
+typedef Scsi_Cmnd *ahd_io_ctx_t;
+
+/******************************* Byte Order ***********************************/
+#define ahd_htobe16(x) cpu_to_be16(x)
+#define ahd_htobe32(x) cpu_to_be32(x)
+#define ahd_htobe64(x) cpu_to_be64(x)
+#define ahd_htole16(x) cpu_to_le16(x)
+#define ahd_htole32(x) cpu_to_le32(x)
+#define ahd_htole64(x) cpu_to_le64(x)
+
+#define ahd_be16toh(x) be16_to_cpu(x)
+#define ahd_be32toh(x) be32_to_cpu(x)
+#define ahd_be64toh(x) be64_to_cpu(x)
+#define ahd_le16toh(x) le16_to_cpu(x)
+#define ahd_le32toh(x) le32_to_cpu(x)
+#define ahd_le64toh(x) le64_to_cpu(x)
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#ifndef BYTE_ORDER
+#if defined(__BIG_ENDIAN)
+#define BYTE_ORDER BIG_ENDIAN
+#endif
+#if defined(__LITTLE_ENDIAN)
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif /* BYTE_ORDER */
+
+/************************* Configuration Data *********************************/
+extern int aic79xx_detect_complete;
+extern Scsi_Host_Template* aic79xx_driver_template;
+
+/***************************** Bus Space/DMA **********************************/
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,17)
+typedef dma_addr_t bus_addr_t;
+#else
+typedef uint32_t bus_addr_t;
+#endif
+typedef uint32_t bus_size_t;
+
+typedef enum {
+ BUS_SPACE_MEMIO,
+ BUS_SPACE_PIO
+} bus_space_tag_t;
+
+typedef union {
+ u_long ioport;
+ volatile uint8_t *maddr;
+} bus_space_handle_t;
+
+typedef struct bus_dma_segment
+{
+ bus_addr_t ds_addr;
+ bus_size_t ds_len;
+} bus_dma_segment_t;
+
+struct ahd_linux_dma_tag
+{
+ bus_size_t alignment;
+ bus_size_t boundary;
+ bus_size_t maxsize;
+};
+typedef struct ahd_linux_dma_tag* bus_dma_tag_t;
+
+struct ahd_linux_dmamap
+{
+ bus_addr_t bus_addr;
+};
+typedef struct ahd_linux_dmamap* bus_dmamap_t;
+
+typedef int bus_dma_filter_t(void*, bus_addr_t);
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+#define BUS_DMA_WAITOK 0x0
+#define BUS_DMA_NOWAIT 0x1
+#define BUS_DMA_ALLOCNOW 0x2
+#define BUS_DMA_LOAD_SEGS 0x4 /*
+ * Argument is an S/G list not
+ * a single buffer.
+ */
+
+#define BUS_SPACE_MAXADDR 0xFFFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT 0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT 0xFFFFFFFF
+
+int ahd_dma_tag_create(struct ahd_softc *, bus_dma_tag_t /*parent*/,
+ bus_size_t /*alignment*/, bus_size_t /*boundary*/,
+ bus_addr_t /*lowaddr*/, bus_addr_t /*highaddr*/,
+ bus_dma_filter_t*/*filter*/, void */*filterarg*/,
+ bus_size_t /*maxsize*/, int /*nsegments*/,
+ bus_size_t /*maxsegsz*/, int /*flags*/,
+ bus_dma_tag_t */*dma_tagp*/);
+
+void ahd_dma_tag_destroy(struct ahd_softc *, bus_dma_tag_t /*tag*/);
+
+int ahd_dmamem_alloc(struct ahd_softc *, bus_dma_tag_t /*dmat*/,
+ void** /*vaddr*/, int /*flags*/,
+ bus_dmamap_t* /*mapp*/);
+
+void ahd_dmamem_free(struct ahd_softc *, bus_dma_tag_t /*dmat*/,
+ void* /*vaddr*/, bus_dmamap_t /*map*/);
+
+void ahd_dmamap_destroy(struct ahd_softc *, bus_dma_tag_t /*tag*/,
+ bus_dmamap_t /*map*/);
+
+int ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t /*dmat*/,
+ bus_dmamap_t /*map*/, void * /*buf*/,
+ bus_size_t /*buflen*/, bus_dmamap_callback_t *,
+ void */*callback_arg*/, int /*flags*/);
+
+int ahd_dmamap_unload(struct ahd_softc *, bus_dma_tag_t, bus_dmamap_t);
+
+/*
+ * Operations performed by ahd_dmamap_sync().
+ */
+#define BUS_DMASYNC_PREREAD 0x01 /* pre-read synchronization */
+#define BUS_DMASYNC_POSTREAD 0x02 /* post-read synchronization */
+#define BUS_DMASYNC_PREWRITE 0x04 /* pre-write synchronization */
+#define BUS_DMASYNC_POSTWRITE 0x08 /* post-write synchronization */
+
+/*
+ * XXX
+ * ahd_dmamap_sync is only used on buffers allocated with
+ * the pci_alloc_consistent() API. Although I'm not sure how
+ * this works on architectures with a write buffer, Linux does
+ * not have an API to sync "coherent" memory. Perhaps we need
+ * to do an mb()?
+ */
+#define ahd_dmamap_sync(ahd, dma_tag, dmamap, offset, len, op)
+
+/************************** SCSI Constants/Structures *************************/
+#define SCSI_REV_2 2
+#define SCSI_STATUS_OK 0x00
+#define SCSI_STATUS_CHECK_COND 0x02
+#define SCSI_STATUS_COND_MET 0x04
+#define SCSI_STATUS_BUSY 0x08
+#define SCSI_STATUS_INTERMED 0x10
+#define SCSI_STATUS_INTERMED_COND_MET 0x14
+#define SCSI_STATUS_RESERV_CONFLICT 0x18
+#define SCSI_STATUS_CMD_TERMINATED 0x22
+#define SCSI_STATUS_QUEUE_FULL 0x28
+
+/*
+ * 6 byte request sense CDB format.
+ */
+struct scsi_sense
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_sense_data
+{
+ uint8_t error_code;
+ uint8_t segment;
+ uint8_t flags;
+ uint8_t info[4];
+ uint8_t extra_len;
+ uint8_t cmd_spec_info[4];
+ uint8_t add_sense_code;
+ uint8_t add_sense_code_qual;
+ uint8_t fru;
+ uint8_t sense_key_spec[3];
+ uint8_t extra_bytes[14];
+};
+
+struct scsi_inquiry
+{
+ u_int8_t opcode;
+ u_int8_t byte2;
+#define SI_EVPD 0x01
+ u_int8_t page_code;
+ u_int8_t reserved;
+ u_int8_t length;
+ u_int8_t control;
+};
+
+struct scsi_inquiry_data
+{
+ uint8_t device;
+#define SID_TYPE(inq_data) ((inq_data)->device & 0x1f)
+#define SID_QUAL(inq_data) (((inq_data)->device & 0xE0) >> 5)
+#define SID_QUAL_LU_CONNECTED 0x00 /*
+ * The specified peripheral device
+ * type is currently connected to
+ * logical unit. If the target cannot
+ * determine whether or not a physical
+ * device is currently connected, it
+ * shall also use this peripheral
+ * qualifier when returning the INQUIRY
+ * data. This peripheral qualifier
+ * does not mean that the device is
+ * ready for access by the initiator.
+ */
+#define SID_QUAL_LU_OFFLINE 0x01 /*
+ * The target is capable of supporting
+ * the specified peripheral device type
+ * on this logical unit; however, the
+ * physical device is not currently
+ * connected to this logical unit.
+ */
+#define SID_QUAL_RSVD 0x02
+#define SID_QUAL_BAD_LU 0x03 /*
+ * The target is not capable of
+ * supporting a physical device on
+ * this logical unit. For this
+ * peripheral qualifier the peripheral
+ * device type shall be set to 1Fh to
+ * provide compatibility with previous
+ * versions of SCSI. All other
+ * peripheral device type values are
+ * reserved for this peripheral
+ * qualifier.
+ */
+#define SID_QUAL_IS_VENDOR_UNIQUE(inq_data) ((SID_QUAL(inq_data) & 0x08) != 0)
+ uint8_t dev_qual2;
+#define SID_QUAL2 0x7F
+#define SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & 0x80) != 0)
+ uint8_t version;
+#define SID_ANSI_REV(inq_data) ((inq_data)->version & 0x07)
+#define SCSI_REV_0 0
+#define SCSI_REV_CCS 1
+#define SCSI_REV_2 2
+#define SCSI_REV_SPC 3
+#define SCSI_REV_SPC2 4
+
+#define SID_ECMA 0x38
+#define SID_ISO 0xC0
+ uint8_t response_format;
+#define SID_AENC 0x80
+#define SID_TrmIOP 0x40
+ uint8_t additional_length;
+ uint8_t reserved[2];
+ uint8_t flags;
+#define SID_SftRe 0x01
+#define SID_CmdQue 0x02
+#define SID_Linked 0x08
+#define SID_Sync 0x10
+#define SID_WBus16 0x20
+#define SID_WBus32 0x40
+#define SID_RelAdr 0x80
+#define SID_VENDOR_SIZE 8
+ char vendor[SID_VENDOR_SIZE];
+#define SID_PRODUCT_SIZE 16
+ char product[SID_PRODUCT_SIZE];
+#define SID_REVISION_SIZE 4
+ char revision[SID_REVISION_SIZE];
+ /*
+ * The following fields were taken from SCSI Primary Commands - 2
+ * (SPC-2) Revision 14, Dated 11 November 1999
+ */
+#define SID_VENDOR_SPECIFIC_0_SIZE 20
+ u_int8_t vendor_specific0[SID_VENDOR_SPECIFIC_0_SIZE];
+ /*
+ * An extension of SCSI Parallel Specific Values
+ */
+#define SID_SPI_IUS 0x01
+#define SID_SPI_QAS 0x02
+#define SID_SPI_CLOCK_ST 0x00
+#define SID_SPI_CLOCK_DT 0x04
+#define SID_SPI_CLOCK_DT_ST 0x0C
+#define SID_SPI_MASK 0x0F
+ uint8_t spi3data;
+ uint8_t reserved2;
+ /*
+ * Version Descriptors, stored 2 byte values.
+ */
+ uint8_t version1[2];
+ uint8_t version2[2];
+ uint8_t version3[2];
+ uint8_t version4[2];
+ uint8_t version5[2];
+ uint8_t version6[2];
+ uint8_t version7[2];
+ uint8_t version8[2];
+
+ uint8_t reserved3[22];
+
+#define SID_VENDOR_SPECIFIC_1_SIZE 160
+ uint8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE];
+};
+
+static __inline uint32_t scsi_4btoul(uint8_t *bytes);
+static __inline uint32_t
+scsi_4btoul(uint8_t *bytes)
+{
+ return ((bytes[0] << 24)|(bytes[1] << 16)|(bytes[2] << 8)|bytes[3]);
+}
+/************************** Timer DataStructures ******************************/
+typedef struct timer_list ahd_timer_t;
+
+/********************************** Includes **********************************/
+/* Host template and function declarations referenced by the template. */
+#include "aic79xx_host.h"
+
+/* Core driver definitions */
+#include "aic79xx.h"
+
+/***************************** Timer Facilities *******************************/
+#define ahd_timer_init init_timer
+#define ahd_timer_stop del_timer
+typedef void ahd_linux_callback_t (u_long);
+static __inline void
+ahd_timer_reset(ahd_timer_t *timer, int usec, ahd_callback_t *func, void *arg)
+{
+ struct ahd_softc *ahd;
+
+ ahd = (struct ahd_softc *)arg;
+ del_timer(timer);
+ timer->data = (u_long)arg;
+ timer->expires = jiffies + (usec * HZ)/1000000;
+ timer->function = (ahd_linux_callback_t*)func;
+ add_timer(timer);
+}
+
+/***************************** SMP support ************************************/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,17)
+#include <linux/spinlock.h>
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+#include <linux/smp.h>
+#endif
+
+#define AIC79XX_DRIVER_VERSION "1.0.0"
+
+/**************************** Front End Queues ********************************/
+/*
+ * Data structure used to cast the Linux struct scsi_cmnd to something
+ * that allows us to use the queue macros. The linux structure has
+ * plenty of space to hold the links fields as required by the queue
+ * macros, but the queue macors require them to have the correct type.
+ */
+struct ahd_cmd_internal {
+ /* Area owned by the Linux scsi layer. */
+ uint8_t private[offsetof(struct scsi_cmnd, SCp.Status)];
+ union {
+ STAILQ_ENTRY(ahd_cmd) ste;
+ LIST_ENTRY(ahd_cmd) le;
+ TAILQ_ENTRY(ahd_cmd) tqe;
+ } links;
+ uint32_t end;
+};
+
+struct ahd_cmd {
+ union {
+ struct ahd_cmd_internal icmd;
+ struct scsi_cmnd scsi_cmd;
+ } un;
+};
+
+#define acmd_icmd(cmd) ((cmd)->un.icmd)
+#define acmd_scsi_cmd(cmd) ((cmd)->un.scsi_cmd)
+#define acmd_links un.icmd.links
+
+/*************************** Device Data Structures ***************************/
+/*
+ * A per probed device structure used to deal with some error recovery
+ * scenarios that the Linux mid-layer code just doesn't know how to
+ * handle. The structure allocated for a device only becomes persistant
+ * after a successfully completed inquiry command to the target when
+ * that inquiry data indicates a lun is present.
+ */
+TAILQ_HEAD(ahd_busyq, ahd_cmd);
+typedef enum {
+ AHD_DEV_UNCONFIGURED = 0x01,
+ AHD_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */
+ AHD_DEV_TIMER_ACTIVE = 0x04, /* Our timer is active */
+ AHD_DEV_ON_RUN_LIST = 0x08, /* Queued to be run later */
+ AHD_DEV_Q_BASIC = 0x10, /* Allow basic device queuing */
+ AHD_DEV_Q_TAGGED = 0x20, /* Allow full SCSI2 command queueing */
+ AHD_DEV_PERIODIC_OTAG = 0x40 /* Send OTAG to prevent starvation */
+} ahd_dev_flags;
+
+struct ahd_linux_target;
+struct ahd_linux_device {
+ TAILQ_ENTRY(ahd_linux_device) links;
+ struct ahd_busyq busyq;
+
+ /*
+ * The number of transactions currently
+ * queued to the device.
+ */
+ int active;
+
+ /*
+ * The currently allowed number of
+ * transactions that can be queued to
+ * the device. Must be signed for
+ * conversion from tagged to untagged
+ * mode where the device may have more
+ * than one outstanding active transaction.
+ */
+ int openings;
+
+ /*
+ * A positive count indicates that this
+ * device's queue is halted.
+ */
+ u_int qfrozen;
+
+ /*
+ * Cumulative command counter.
+ */
+ u_long commands_issued;
+
+ /*
+ * The number of tagged transactions when
+ * running at our current opening level
+ * that have been successfully received by
+ * this device since the last QUEUE FULL.
+ */
+ u_int tag_success_count;
+#define AHD_TAG_SUCCESS_INTERVAL 50
+
+ ahd_dev_flags flags;
+
+ /*
+ * Per device timer.
+ */
+ struct timer_list timer;
+
+ /*
+ * The high limit for the tags variable.
+ */
+ u_int maxtags;
+
+ /*
+ * The computed number of tags outstanding
+ * at the time of the last QUEUE FULL event.
+ */
+ u_int tags_on_last_queuefull;
+
+ /*
+ * How many times we have seen a queue full
+ * with the same number of tags. This is used
+ * to stop our adaptive queue depth algorithm
+ * on devices with a fixed number of tags.
+ */
+ u_int last_queuefull_same_count;
+#define AHD_LOCK_TAGS_COUNT 50
+
+ /*
+ * How many transactions have been queued
+ * without the device going idle. We use
+ * this statistic to determine when to issue
+ * an ordered tag to prevent transaction
+ * starvation. This statistic is only updated
+ * if the AHD_DEV_PERIODIC_OTAG flag is set
+ * on this device.
+ */
+ u_int commands_since_idle_or_otag;
+#define AHD_OTAG_THRESH 500
+
+ int lun;
+ struct ahd_linux_target *target;
+};
+
+struct ahd_linux_target {
+ struct ahd_linux_device *devices[AHD_NUM_LUNS];
+ int channel;
+ int target;
+ int refcount;
+ struct ahd_transinfo last_tinfo;
+ struct ahd_softc *ahd;
+};
+
+/********************* Definitions Required by the Core ***********************/
+/*
+ * Number of SG segments we require. So long as the S/G segments for
+ * a particular transaction are allocated in a physically contiguous
+ * manner and are allocated below 4GB, the number of S/G segments is
+ * unrestricted.
+ */
+#define AHD_NSEG 128
+
+/*
+ * Per-SCB OSM storage.
+ */
+struct scb_platform_data {
+ struct ahd_linux_device *dev;
+ bus_addr_t buf_busaddr;
+ uint32_t xfer_len;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+ uint32_t resid; /* Transfer residual */
+#endif
+};
+
+/*
+ * Define a structure used for each host adapter. All members are
+ * aligned on a boundary >= the size of the member to honor the
+ * alignment restrictions of the various platforms supported by
+ * this driver.
+ */
+TAILQ_HEAD(ahd_completeq, ahd_cmd);
+struct ahd_platform_data {
+ /*
+ * Fields accessed from interrupt context.
+ */
+ struct ahd_linux_target *targets[AHD_NUM_TARGETS];
+ TAILQ_HEAD(, ahd_linux_device) device_runq;
+ struct ahd_completeq completeq;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ spinlock_t spin_lock;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ struct tasklet_struct runq_tasklet;
+#endif
+ u_int qfrozen;
+ struct timer_list reset_timer;
+ struct semaphore eh_sem;
+ struct Scsi_Host *host; /* pointer to scsi host */
+#define AHD_LINUX_NOIRQ ((uint32_t)~0)
+ uint32_t irq; /* IRQ for this adapter */
+ uint32_t bios_address;
+ uint32_t mem_busaddr; /* Mem Base Addr */
+ bus_addr_t hw_dma_mask;
+};
+
+/************************** OS Utility Wrappers *******************************/
+#define printf printk
+#define M_NOWAIT GFP_ATOMIC
+#define M_WAITOK 0
+#define malloc(size, type, flags) kmalloc(size, flags)
+#define free(ptr, type) kfree(ptr)
+
+static __inline void ahd_delay(long);
+static __inline void
+ahd_delay(long usec)
+{
+ /*
+ * udelay on Linux can have problems for
+ * multi-millisecond waits. Wait at most
+ * 1024us per call.
+ */
+ while (usec > 0) {
+ udelay(usec % 1024);
+ usec -= 1024;
+ }
+}
+
+
+/***************************** Low Level I/O **********************************/
+#if defined(__powerpc__) || defined(__i386__) || defined(__ia64__)
+#define MMAPIO
+#endif
+
+static __inline uint8_t ahd_inb(struct ahd_softc * ahd, long port);
+static __inline uint16_t ahd_inw_atomic(struct ahd_softc * ahd, long port);
+static __inline void ahd_outb(struct ahd_softc * ahd, long port, uint8_t val);
+static __inline void ahd_outw_atomic(struct ahd_softc * ahd,
+ long port, uint16_t val);
+static __inline void ahd_outsb(struct ahd_softc * ahd, long port,
+ uint8_t *, int count);
+static __inline void ahd_insb(struct ahd_softc * ahd, long port,
+ uint8_t *, int count);
+
+static __inline uint8_t
+ahd_inb(struct ahd_softc * ahd, long port)
+{
+ uint8_t x;
+#ifdef MMAPIO
+
+ if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+ x = readb(ahd->bshs[0].maddr + port);
+ } else {
+ x = inb(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+ }
+#else
+ x = inb(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+#endif
+ mb();
+ return (x);
+}
+
+static __inline uint16_t
+ahd_inw_atomic(struct ahd_softc * ahd, long port)
+{
+ uint8_t x;
+#ifdef MMAPIO
+
+ if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+ x = readw(ahd->bshs[0].maddr + port);
+ } else {
+ x = inw(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+ }
+#else
+ x = inw(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+#endif
+ mb();
+ return (x);
+}
+
+static __inline void
+ahd_outb(struct ahd_softc * ahd, long port, uint8_t val)
+{
+#ifdef MMAPIO
+ if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+ writeb(val, ahd->bshs[0].maddr + port);
+ } else {
+ outb(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+ }
+#else
+ outb(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+#endif
+ mb();
+}
+
+static __inline void
+ahd_outw_atomic(struct ahd_softc * ahd, long port, uint16_t val)
+{
+#ifdef MMAPIO
+ if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+ writew(val, ahd->bshs[0].maddr + port);
+ } else {
+ outw(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+ }
+#else
+ outw(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+#endif
+ mb();
+}
+
+static __inline void
+ahd_outsb(struct ahd_softc * ahd, long port, uint8_t *array, int count)
+{
+ int i;
+
+ /*
+ * There is probably a more efficient way to do this on Linux
+ * but we don't use this for anything speed critical and this
+ * should work.
+ */
+ for (i = 0; i < count; i++)
+ ahd_outb(ahd, port, *array++);
+}
+
+static __inline void
+ahd_insb(struct ahd_softc * ahd, long port, uint8_t *array, int count)
+{
+ int i;
+
+ /*
+ * There is probably a more efficient way to do this on Linux
+ * but we don't use this for anything speed critical and this
+ * should work.
+ */
+ for (i = 0; i < count; i++)
+ *array++ = ahd_inb(ahd, port);
+}
+
+/**************************** Initialization **********************************/
+int ahd_linux_register_host(struct ahd_softc *,
+ Scsi_Host_Template *);
+
+uint64_t ahd_linux_get_memsize(void);
+
+/*************************** Pretty Printing **********************************/
+struct info_str {
+ char *buffer;
+ int length;
+ off_t offset;
+ int pos;
+};
+
+void ahd_format_transinfo(struct info_str *info,
+ struct ahd_transinfo *tinfo);
+
+/******************************** Locking *************************************/
+/* Lock protecting internal data structures */
+static __inline void ahd_lockinit(struct ahd_softc *);
+static __inline void ahd_lock(struct ahd_softc *, unsigned long *flags);
+static __inline void ahd_unlock(struct ahd_softc *, unsigned long *flags);
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void ahd_done_lockinit(struct ahd_softc *);
+static __inline void ahd_done_lock(struct ahd_softc *, unsigned long *flags);
+static __inline void ahd_done_unlock(struct ahd_softc *, unsigned long *flags);
+
+/* Lock held during ahd_list manipulation and ahd softc frees */
+extern spinlock_t ahd_list_spinlock;
+static __inline void ahd_list_lockinit(void);
+static __inline void ahd_list_lock(unsigned long *flags);
+static __inline void ahd_list_unlock(unsigned long *flags);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+static __inline void
+ahd_lockinit(struct ahd_softc *ahd)
+{
+ spin_lock_init(&ahd->platform_data->spin_lock);
+}
+
+static __inline void
+ahd_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ *flags = 0;
+ spin_lock_irqsave(&ahd->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahd_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ spin_unlock_irqrestore(&ahd->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahd_done_lockinit(struct ahd_softc *ahd)
+{
+ /* We don't own the iorequest lock, so we don't initialize it. */
+}
+
+static __inline void
+ahd_done_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ *flags = 0;
+ spin_lock_irqsave(&io_request_lock, *flags);
+}
+
+static __inline void
+ahd_done_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ spin_unlock_irqrestore(&io_request_lock, *flags);
+}
+
+static __inline void
+ahd_list_lockinit()
+{
+ spin_lock_init(&ahd_list_spinlock);
+}
+
+static __inline void
+ahd_list_lock(unsigned long *flags)
+{
+ *flags = 0;
+ spin_lock_irqsave(&ahd_list_spinlock, *flags);
+}
+
+static __inline void
+ahd_list_unlock(unsigned long *flags)
+{
+ spin_unlock_irqrestore(&ahd_list_spinlock, *flags);
+}
+
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+ahd_lockinit(struct ahd_softc *ahd)
+{
+}
+
+static __inline void
+ahd_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ *flags = 0;
+ save_flags(*flags);
+ cli();
+}
+
+static __inline void
+ahd_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ restore_flags(*flags);
+}
+
+ahd_done_lockinit(struct ahd_softc *ahd)
+{
+}
+
+static __inline void
+ahd_done_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ /*
+ * The done lock is always held while
+ * the ahd lock is held so blocking
+ * interrupts again would have no effect.
+ */
+}
+
+static __inline void
+ahd_done_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+}
+
+static __inline void
+ahd_list_lockinit()
+{
+}
+
+static __inline void
+ahd_list_lock(unsigned long *flags)
+{
+ *flags = 0;
+ save_flags(*flags);
+ cli();
+}
+
+static __inline void
+ahd_list_unlock(unsigned long *flags)
+{
+ restore_flags(*flags);
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+/******************************* PCI Definitions ******************************/
+/*
+ * PCIM_xxx: mask to locate subfield in register
+ * PCIR_xxx: config register offset
+ * PCIC_xxx: device class
+ * PCIS_xxx: device subclass
+ * PCIP_xxx: device programming interface
+ * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices)
+ * PCID_xxx: device ID
+ */
+#define PCIR_DEVVENDOR 0x00
+#define PCIR_VENDOR 0x00
+#define PCIR_DEVICE 0x02
+#define PCIR_COMMAND 0x04
+#define PCIM_CMD_PORTEN 0x0001
+#define PCIM_CMD_MEMEN 0x0002
+#define PCIM_CMD_BUSMASTEREN 0x0004
+#define PCIM_CMD_MWRICEN 0x0010
+#define PCIM_CMD_PERRESPEN 0x0040
+#define PCIM_CMD_SERRESPEN 0x0100
+#define PCIR_STATUS 0x06
+#define PCIR_REVID 0x08
+#define PCIR_PROGIF 0x09
+#define PCIR_SUBCLASS 0x0a
+#define PCIR_CLASS 0x0b
+#define PCIR_CACHELNSZ 0x0c
+#define PCIR_LATTIMER 0x0d
+#define PCIR_HEADERTYPE 0x0e
+#define PCIM_MFDEV 0x80
+#define PCIR_BIST 0x0f
+#define PCIR_CAP_PTR 0x34
+
+/* config registers for header type 0 devices */
+#define PCIR_MAPS 0x10
+#define PCIR_SUBVEND_0 0x2c
+#define PCIR_SUBDEV_0 0x2e
+
+/****************************** PCI-X definitions *****************************/
+#define PCIXR_COMMAND 0x96
+#define PCIXR_DEVADDR 0x98
+#define PCIXM_DEVADDR_FNUM 0x0003 /* Function Number */
+#define PCIXM_DEVADDR_DNUM 0x00F8 /* Device Number */
+#define PCIXM_DEVADDR_BNUM 0xFF00 /* Bus Number */
+#define PCIXR_STATUS 0x9A
+#define PCIXM_STATUS_64BIT 0x0001 /* Active 64bit connection to device. */
+#define PCIXM_STATUS_133CAP 0x0002 /* Device is 133MHz capable */
+#define PCIXM_STATUS_SCDISC 0x0004 /* Split Completion Discarded */
+#define PCIXM_STATUS_UNEXPSC 0x0008 /* Unexpected Split Completion */
+#define PCIXM_STATUS_CMPLEXDEV 0x0010 /* Device Complexity (set == bridge) */
+#define PCIXM_STATUS_MAXMRDBC 0x0060 /* Maximum Burst Read Count */
+#define PCIXM_STATUS_MAXSPLITS 0x0380 /* Maximum Split Transactions */
+#define PCIXM_STATUS_MAXCRDS 0x1C00 /* Maximum Cumulative Read Size */
+#define PCIXM_STATUS_RCVDSCEM 0x2000 /* Received a Split Comp w/Error msg */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+extern struct pci_driver aic79xx_pci_driver;
+#endif
+
+typedef enum
+{
+ AHD_POWER_STATE_D0,
+ AHD_POWER_STATE_D1,
+ AHD_POWER_STATE_D2,
+ AHD_POWER_STATE_D3
+} ahd_power_state;
+
+void ahd_power_state_change(struct ahd_softc *ahd,
+ ahd_power_state new_state);
+
+/******************************* PCI Routines *********************************/
+/*
+ * We need to use the bios32.h routines if we are kernel version 2.1.92 or less.
+ */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92)
+#if defined(__sparc_v9__) || defined(__powerpc__)
+#error "PPC and Sparc platforms are only support under 2.1.92 and above"
+#endif
+#include <linux/bios32.h>
+#endif
+
+int ahd_linux_pci_probe(Scsi_Host_Template *);
+int ahd_pci_map_registers(struct ahd_softc *ahd);
+int ahd_pci_map_int(struct ahd_softc *ahd);
+
+static __inline uint32_t ahd_pci_read_config(ahd_dev_softc_t pci,
+ int reg, int width);
+
+static __inline uint32_t
+ahd_pci_read_config(ahd_dev_softc_t pci, int reg, int width)
+{
+ switch (width) {
+ case 1:
+ {
+ uint8_t retval;
+
+ pci_read_config_byte(pci, reg, &retval);
+ return (retval);
+ }
+ case 2:
+ {
+ uint16_t retval;
+ pci_read_config_word(pci, reg, &retval);
+ return (retval);
+ }
+ case 4:
+ {
+ uint32_t retval;
+ pci_read_config_dword(pci, reg, &retval);
+ return (retval);
+ }
+ default:
+ panic("ahd_pci_read_config: Read size too big");
+ /* NOTREACHED */
+ return (0);
+ }
+}
+
+static __inline void ahd_pci_write_config(ahd_dev_softc_t pci,
+ int reg, uint32_t value,
+ int width);
+
+static __inline void
+ahd_pci_write_config(ahd_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+ switch (width) {
+ case 1:
+ pci_write_config_byte(pci, reg, value);
+ break;
+ case 2:
+ pci_write_config_word(pci, reg, value);
+ break;
+ case 4:
+ pci_write_config_dword(pci, reg, value);
+ break;
+ default:
+ panic("ahd_pci_write_config: Write size too big");
+ /* NOTREACHED */
+ }
+}
+
+static __inline int ahd_get_pci_function(ahd_dev_softc_t);
+static __inline int
+ahd_get_pci_function(ahd_dev_softc_t pci)
+{
+ return (PCI_FUNC(pci->devfn));
+}
+
+static __inline int ahd_get_pci_slot(ahd_dev_softc_t);
+static __inline int
+ahd_get_pci_slot(ahd_dev_softc_t pci)
+{
+ return (PCI_SLOT(pci->devfn));
+}
+
+static __inline int ahd_get_pci_bus(ahd_dev_softc_t);
+static __inline int
+ahd_get_pci_bus(ahd_dev_softc_t pci)
+{
+ return (pci->bus->number);
+}
+
+static __inline void ahd_flush_device_writes(struct ahd_softc *);
+static __inline void
+ahd_flush_device_writes(struct ahd_softc *ahd)
+{
+ /* XXX Is this sufficient for all architectures??? */
+ ahd_inb(ahd, INTSTAT);
+}
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,0)
+#define pci_map_sg(pdev, sg_list, nseg, direction) (nseg)
+#define pci_unmap_sg(pdev, sg_list, nseg, direction)
+#define sg_dma_address(sg) (VIRT_TO_BUS((sg)->address))
+#define sg_dma_len(sg) ((sg)->length)
+#define pci_map_single(pdev, buffer, bufflen, direction) \
+ (VIRT_TO_BUS(buffer))
+#define pci_unmap_single(pdev, buffer, buflen, direction)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+#define ahd_pci_set_dma_mask pci_set_dma_mask
+#else
+/*
+ * Always "return" 0 for success.
+ */
+#define ahd_pci_set_dma_mask(dev_softc, mask) \
+ (((dev_softc)->dma_mask = mask) && 0)
+#endif
+/*********************** Transaction Access Wrappers **************************/
+static __inline void ahd_set_transaction_status(struct scb *, uint32_t);
+static __inline
+void ahd_set_transaction_status(struct scb *scb, uint32_t status)
+{
+ scb->io_ctx->result &= ~(CAM_STATUS_MASK << 16);
+ scb->io_ctx->result |= status << 16;
+}
+
+static __inline void ahd_set_scsi_status(struct scb *, uint32_t);
+static __inline
+void ahd_set_scsi_status(struct scb *scb, uint32_t status)
+{
+ scb->io_ctx->result &= ~0xFFFF;
+ scb->io_ctx->result |= status;
+}
+
+static __inline uint32_t ahd_get_transaction_status(struct scb *);
+static __inline
+uint32_t ahd_get_transaction_status(struct scb *scb)
+{
+ return ((scb->io_ctx->result >> 16) & CAM_STATUS_MASK);
+}
+
+static __inline uint32_t ahd_get_scsi_status(struct scb *);
+static __inline
+uint32_t ahd_get_scsi_status(struct scb *scb)
+{
+ return (scb->io_ctx->result & 0xFFFF);
+}
+
+static __inline void ahd_set_transaction_tag(struct scb *, int, u_int);
+static __inline
+void ahd_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+ /*
+ * Nothing to do for linux as the incoming transaction
+ * has no concept of tag/non tagged, etc.
+ */
+}
+
+static __inline u_long ahd_get_transfer_length(struct scb *);
+static __inline
+u_long ahd_get_transfer_length(struct scb *scb)
+{
+ return (scb->platform_data->xfer_len);
+}
+
+static __inline int ahd_get_transfer_dir(struct scb *);
+static __inline
+int ahd_get_transfer_dir(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,40)
+ return (scb->io_ctx->sc_data_direction);
+#else
+ if (scb->io_ctx->bufflen == 0)
+ return (CAM_DIR_NONE);
+
+ switch(scb->io_ctx->cmnd[0]) {
+ case 0x08: /* READ(6) */
+ case 0x28: /* READ(10) */
+ case 0xA8: /* READ(12) */
+ return (CAM_DIR_IN);
+ case 0x0A: /* WRITE(6) */
+ case 0x2A: /* WRITE(10) */
+ case 0xAA: /* WRITE(12) */
+ return (CAM_DIR_OUT);
+ default:
+ return (CAM_DIR_NONE);
+ }
+#endif
+}
+
+static __inline void ahd_set_residual(struct scb *, u_long);
+static __inline
+void ahd_set_residual(struct scb *scb, u_long resid)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ scb->io_ctx->resid = resid;
+#else
+ scb->platform_data->resid = resid;
+#endif
+}
+
+static __inline void ahd_set_sense_residual(struct scb *, u_long);
+static __inline
+void ahd_set_sense_residual(struct scb *scb, u_long resid)
+{
+ /* This can't be reported in Linux */
+}
+
+static __inline u_long ahd_get_residual(struct scb *);
+static __inline
+u_long ahd_get_residual(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ return (scb->io_ctx->resid);
+#else
+ return (scb->platform_data->resid);
+#endif
+}
+
+static __inline int ahd_perform_autosense(struct scb *);
+static __inline
+int ahd_perform_autosense(struct scb *scb)
+{
+ /*
+ * We always perform autosense in Linux.
+ * On other platforms this is set on a
+ * per-transaction basis.
+ */
+ return (1);
+}
+
+static __inline uint32_t
+ahd_get_sense_bufsize(struct ahd_softc *ahd, struct scb *scb)
+{
+ return (sizeof(struct scsi_sense_data));
+}
+
+static __inline void ahd_notify_xfer_settings_change(struct ahd_softc *,
+ struct ahd_devinfo *);
+static __inline void
+ahd_notify_xfer_settings_change(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo)
+{
+ /* Nothing to do here for linux */
+}
+
+static __inline void ahd_platform_scb_free(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void
+ahd_platform_scb_free(struct ahd_softc *ahd, struct scb *scb)
+{
+ ahd->flags &= ~AHD_RESOURCE_SHORTAGE;
+}
+
+int ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg);
+void ahd_platform_free(struct ahd_softc *ahd);
+void ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb);
+void ahd_freeze_simq(struct ahd_softc *ahd);
+void ahd_release_simq(struct ahd_softc *ahd);
+static __inline void ahd_freeze_scb(struct scb *scb);
+static __inline void
+ahd_freeze_scb(struct scb *scb)
+{
+ /* Noting to do here for linux */
+}
+
+void ahd_platform_set_tags(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo, ahd_queue_alg);
+int ahd_platform_abort_scbs(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+void ahd_linux_isr(int irq, void *dev_id, struct pt_regs * regs);
+void ahd_platform_flushwork(struct ahd_softc *ahd);
+int ahd_softc_comp(struct ahd_softc *, struct ahd_softc *);
+void ahd_done(struct ahd_softc*, struct scb*);
+void ahd_send_async(struct ahd_softc *, char channel,
+ u_int target, u_int lun, ac_code, void *);
+void ahd_print_path(struct ahd_softc *, struct scb *);
+void ahd_platform_dump_card_state(struct ahd_softc *ahd);
+
+#ifdef CONFIG_PCI
+#define AHD_PCI_CONFIG 1
+#else
+#define AHD_PCI_CONFIG 0
+#endif
+#define bootverbose aic79xx_verbose
+extern int aic79xx_verbose;
+#endif /* _AIC79XX_LINUX_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/xen/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
new file mode 100644
index 0000000000..53cf44e6ef
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
@@ -0,0 +1,428 @@
+/*
+ * Linux driver attachment glue for PCI based U320 controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm_pci.c#11 $
+ */
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+struct pci_device_id
+{
+};
+#endif
+
+static int ahd_linux_pci_dev_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+static int ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd,
+ u_long *base, u_long *base2);
+static int ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
+ u_long *bus_addr,
+ uint8_t **maddr);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static void ahd_linux_pci_dev_remove(struct pci_dev *pdev);
+
+/* We do our own ID filtering. So, grab all SCSI storage class devices. */
+static struct pci_device_id ahd_linux_pci_id_table[] = {
+ {
+ 0x9005, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ahd_linux_pci_id_table);
+
+struct pci_driver aic79xx_pci_driver = {
+ name: "aic79xx",
+ probe: ahd_linux_pci_dev_probe,
+ remove: ahd_linux_pci_dev_remove,
+ id_table: ahd_linux_pci_id_table
+};
+
+static void
+ahd_linux_pci_dev_remove(struct pci_dev *pdev)
+{
+ struct ahd_softc *ahd;
+ u_long l;
+
+ /*
+ * We should be able to just perform
+ * the free directly, but check our
+ * list for extra sanity.
+ */
+ ahd_list_lock(&l);
+ ahd = ahd_find_softc((struct ahd_softc *)pdev->driver_data);
+ if (ahd != NULL) {
+ u_long s;
+
+ ahd_lock(ahd, &s);
+ ahd_intr_enable(ahd, FALSE);
+ ahd_unlock(ahd, &s);
+ ahd_free(ahd);
+ }
+ ahd_list_unlock(&l);
+}
+#endif /* !LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */
+
+static int
+ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ char buf[80];
+ struct ahd_softc *ahd;
+ ahd_dev_softc_t pci;
+ struct ahd_pci_identity *entry;
+ char *name;
+ int error;
+
+ /*
+ * Some BIOSen report the same device multiple times.
+ */
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+ struct pci_dev *probed_pdev;
+
+ probed_pdev = ahd->dev_softc;
+ if (probed_pdev->bus->number == pdev->bus->number
+ && probed_pdev->devfn == pdev->devfn)
+ break;
+ }
+ if (ahd != NULL) {
+ /* Skip duplicate. */
+ return (-ENODEV);
+ }
+
+ pci = pdev;
+ entry = ahd_find_pci_device(pci);
+ if (entry == NULL)
+ return (-ENODEV);
+
+ /*
+ * Allocate a softc for this card and
+ * set it up for attachment by our
+ * common detect routine.
+ */
+ sprintf(buf, "ahd_pci:%d:%d:%d",
+ ahd_get_pci_bus(pci),
+ ahd_get_pci_slot(pci),
+ ahd_get_pci_function(pci));
+ name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (name == NULL)
+ return (-ENOMEM);
+ strcpy(name, buf);
+ ahd = ahd_alloc(NULL, name);
+ if (ahd == NULL)
+ return (-ENOMEM);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if (pci_enable_device(pdev)) {
+ ahd_free(ahd);
+ return (-ENODEV);
+ }
+ pci_set_master(pdev);
+
+ if (sizeof(bus_addr_t) > 4) {
+ uint64_t memsize;
+
+ memsize = ahd_linux_get_memsize();
+ if (memsize >= 0x8000000000
+ && ahd_pci_set_dma_mask(pdev, 0xFFFFFFFFFFFFFFFFULL) == 0) {
+ ahd->flags |= AHD_64BIT_ADDRESSING;
+ ahd->platform_data->hw_dma_mask =
+ (bus_addr_t)(0xFFFFFFFFFFFFFFFFULL&(bus_addr_t)~0);
+ } else if (memsize > 0x80000000
+ && ahd_pci_set_dma_mask(pdev, 0x7FFFFFFFFFULL) == 0) {
+ ahd->flags |= AHD_39BIT_ADDRESSING;
+ ahd->platform_data->hw_dma_mask =
+ (bus_addr_t)(0x7FFFFFFFFFULL & (bus_addr_t)~0);
+ }
+ }
+#endif
+ ahd->dev_softc = pci;
+ error = ahd_pci_config(ahd, entry);
+ if (error != 0) {
+ ahd_free(ahd);
+ return (-error);
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pdev->driver_data = ahd;
+ if (aic79xx_detect_complete)
+ ahd_linux_register_host(ahd, aic79xx_driver_template);
+#endif
+ return (0);
+}
+
+int
+ahd_linux_pci_probe(Scsi_Host_Template *template)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ return (pci_module_init(&aic79xx_pci_driver));
+#else
+ struct pci_dev *pdev;
+ u_int class;
+ int found;
+
+ /* If we don't have a PCI bus, we can't find any adapters. */
+ if (pci_present() == 0)
+ return (0);
+
+ found = 0;
+ pdev = NULL;
+ class = PCI_CLASS_STORAGE_SCSI << 8;
+ while ((pdev = pci_find_class(class, pdev)) != NULL) {
+ ahd_dev_softc_t pci;
+ int error;
+
+ pci = pdev;
+ error = ahd_linux_pci_dev_probe(pdev, /*pci_devid*/NULL);
+ if (error == 0)
+ found++;
+ }
+ return (found);
+#endif
+}
+
+static int
+ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, u_long *base,
+ u_long *base2)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ *base = pci_resource_start(ahd->dev_softc, 0);
+ /*
+ * This is really the 3rd bar and should be at index 2,
+ * but the Linux PCI code doesn't know how to "count" 64bit
+ * bars.
+ */
+ *base2 = pci_resource_start(ahd->dev_softc, 3);
+#else
+ *base = ahd_pci_read_config(ahd->dev_softc, AHD_PCI_IOADDR0, 4);
+ *base2 = ahd_pci_read_config(ahd->dev_softc, AHD_PCI_IOADDR1, 4);
+ *base &= PCI_BASE_ADDRESS_IO_MASK;
+ *base2 &= PCI_BASE_ADDRESS_IO_MASK;
+#endif
+ if (*base == 0 || *base2 == 0)
+ return (ENOMEM);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+ if (check_region(*base, 256) != 0
+ || check_region(*base2, 256) != 0)
+ return (ENOMEM);
+ else {
+ request_region(*base, 256, "aic79xx");
+ request_region(*base2, 256, "aic79xx");
+ }
+#else
+ if (request_region(*base, 256, "aic79xx") == 0)
+ return (ENOMEM);
+ if (request_region(*base2, 256, "aic79xx") == 0) {
+ release_region(*base2, 256);
+ return (ENOMEM);
+ }
+#endif
+ return (0);
+}
+
+static int
+ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
+ u_long *bus_addr,
+ uint8_t **maddr)
+{
+ u_long start;
+ u_long base_page;
+ u_long base_offset;
+ int error;
+
+ if ((ahd->chip & AHD_BUS_MASK) == AHD_PCIX
+ && (ahd->bugs & AHD_PCIX_MMAPIO_BUG) != 0)
+ return (ENOMEM);
+
+ error = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ start = pci_resource_start(ahd->dev_softc, 1);
+ base_page = start & PAGE_MASK;
+ base_offset = start - base_page;
+#else
+ start = ahd_pci_read_config(ahd->dev_softc, PCIR_MAPS+4, 4);
+ base_offset = start & PCI_BASE_ADDRESS_MEM_MASK;
+ base_page = base_offset & PAGE_MASK;
+ base_offset -= base_page;
+#endif
+ if (start != 0) {
+ *bus_addr = start;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if (request_mem_region(start, 0x1000, "aic79xx") == 0)
+ error = ENOMEM;
+#endif
+ if (error == 0) {
+ *maddr = ioremap_nocache(base_page, base_offset + 256);
+ if (*maddr == NULL) {
+ error = ENOMEM;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ release_mem_region(start, 0x1000);
+#endif
+ } else
+ *maddr += base_offset;
+ }
+ } else
+ error = ENOMEM;
+ return (error);
+}
+
+int
+ahd_pci_map_registers(struct ahd_softc *ahd)
+{
+ uint32_t command;
+ u_long base;
+ uint8_t *maddr;
+ int error;
+
+ /*
+ * If its allowed, we prefer memory mapped access.
+ */
+ command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, 4);
+ command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
+ base = 0;
+ maddr = NULL;
+#ifdef MMAPIO
+ error = ahd_linux_pci_reserve_mem_region(ahd, &base, &maddr);
+ if (error == 0) {
+ ahd->platform_data->mem_busaddr = base;
+ ahd->tags[0] = BUS_SPACE_MEMIO;
+ ahd->bshs[0].maddr = maddr;
+ ahd->tags[1] = BUS_SPACE_MEMIO;
+ ahd->bshs[1].maddr = maddr + 0x100;
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+ command | PCIM_CMD_MEMEN, 4);
+
+ /*
+ * Do a quick test to see if memory mapped
+ * I/O is functioning correctly.
+ */
+ if (ahd_inb(ahd, HCNTRL) == 0xFF) {
+
+ printf("aic79xx: PCI Device %d:%d:%d "
+ "failed memory mapped test\n",
+ ahd_get_pci_bus(ahd->dev_softc),
+ ahd_get_pci_slot(ahd->dev_softc),
+ ahd_get_pci_function(ahd->dev_softc));
+ ahd->bshs[0].maddr = NULL;
+ maddr = NULL;
+ } else
+ command |= PCIM_CMD_MEMEN;
+ } else {
+ printf("aic79xx: PCI%d:%d:%d MEM region 0x%lx "
+ "unavailable. Cannot memory map device.\n",
+ ahd_get_pci_bus(ahd->dev_softc),
+ ahd_get_pci_slot(ahd->dev_softc),
+ ahd_get_pci_function(ahd->dev_softc),
+ base);
+ }
+#endif
+
+ if (maddr == NULL) {
+ u_long base2;
+
+ error = ahd_linux_pci_reserve_io_regions(ahd, &base, &base2);
+ if (error == 0) {
+ ahd->tags[0] = BUS_SPACE_PIO;
+ ahd->tags[1] = BUS_SPACE_PIO;
+ ahd->bshs[0].ioport = base;
+ ahd->bshs[1].ioport = base2;
+ command |= PCIM_CMD_PORTEN;
+ } else {
+ printf("aic79xx: PCI%d:%d:%d IO regions 0x%lx and 0x%lx"
+ "unavailable. Cannot map device.\n",
+ ahd_get_pci_bus(ahd->dev_softc),
+ ahd_get_pci_slot(ahd->dev_softc),
+ ahd_get_pci_function(ahd->dev_softc),
+ base, base2);
+ }
+ }
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, 4);
+ return (error);
+}
+
+int
+ahd_pci_map_int(struct ahd_softc *ahd)
+{
+ int error;
+
+ error = request_irq(ahd->dev_softc->irq, ahd_linux_isr,
+ SA_SHIRQ, "aic79xx", ahd);
+ if (error == 0)
+ ahd->platform_data->irq = ahd->dev_softc->irq;
+
+ return (-error);
+}
+
+void
+ahd_power_state_change(struct ahd_softc *ahd, ahd_power_state new_state)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pci_set_power_state(ahd->dev_softc, new_state);
+#else
+ uint32_t cap;
+ u_int cap_offset;
+
+ /*
+ * Traverse the capability list looking for
+ * the power management capability.
+ */
+ cap = 0;
+ cap_offset = ahd_pci_read_config(ahd->dev_softc,
+ PCIR_CAP_PTR, /*bytes*/1);
+ while (cap_offset != 0) {
+
+ cap = ahd_pci_read_config(ahd->dev_softc,
+ cap_offset, /*bytes*/4);
+ if ((cap & 0xFF) == 1
+ && ((cap >> 16) & 0x3) > 0) {
+ uint32_t pm_control;
+
+ pm_control = ahd_pci_read_config(ahd->dev_softc,
+ cap_offset + 4,
+ /*bytes*/4);
+ pm_control &= ~0x3;
+ pm_control |= new_state;
+ ahd_pci_write_config(ahd->dev_softc,
+ cap_offset + 4,
+ pm_control, /*bytes*/2);
+ break;
+ }
+ cap_offset = (cap >> 8) & 0xFF;
+ }
+#endif
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_pci.c b/xen/drivers/scsi/aic7xxx/aic79xx_pci.c
new file mode 100644
index 0000000000..b7bd3cc73d
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_pci.c
@@ -0,0 +1,792 @@
+/*
+ * Product specific probe and attach routines for:
+ * aic7901 and aic7902 SCSI controllers
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx_pci.c#32 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#else
+#include <dev/aic7xxx/aic79xx_osm.h>
+#include <dev/aic7xxx/aic79xx_inline.h>
+#endif
+
+static __inline uint64_t
+ahd_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+ uint64_t id;
+
+ id = subvendor
+ | (subdevice << 16)
+ | ((uint64_t)vendor << 32)
+ | ((uint64_t)device << 48);
+
+ return (id);
+}
+
+#define ID_ALL_MASK 0xFFFFFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK 0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK 0xFFF0FFFF00000000ull
+
+#define ID_AIC7901 0x800F9005FFFF9005ull
+#define ID_AIC7901_IROC 0x80089005FFFF9005ull
+#define ID_AHA_29320 0x8000900500609005ull
+#define ID_AHA_29320LP 0x8000900500409005ull
+
+#define ID_AIC7902 0x801F9005FFFF9005ull
+#define ID_AIC7902_IROC 0x80189005FFFF9005ull
+#define ID_AHA_39320 0x8010900500409005ull
+#define ID_AHA_39320D 0x8011900500419005ull
+#define ID_AHA_39320D_CPQ 0x8011900500AC0E11ull
+#define ID_AIC7902_PCI_REV_A3 0x2
+#define ID_AIC7902_PCI_REV_A4 0x3
+#define ID_AIC7902_PCI_REV_B0 0xFF /* Rev Id not yet known. */
+#define SUBID_CPQ 0x0E11
+
+#define DEVID_9005_TYPE(id) ((id) & 0xF)
+#define DEVID_9005_TYPE_HBA 0x0 /* Standard Card */
+#define DEVID_9005_TYPE_HBA_2EXT 0x1 /* 2 External Ports */
+#define DEVID_9005_TYPE_IROC 0x8 /* Raid(0,1,10) Card */
+#define DEVID_9005_TYPE_MB 0xF /* On Motherboard */
+
+#define DEVID_9005_MFUNC(id) ((id) & 0x10)
+
+#define DEVID_9005_PACKETIZED(id) ((id) & 0x8000)
+
+#define SUBID_9005_TYPE(id) ((id) & 0xF)
+#define SUBID_9005_TYPE_HBA 0x0 /* Standard Card */
+#define SUBID_9005_TYPE_MB 0xF /* On Motherboard */
+
+#define SUBID_9005_AUTOTERM(id) (((id) & 0x10) == 0)
+
+#define SUBID_9005_LEGACYCONN_FUNC(id) ((id) & 0x20)
+
+#define SUBID_9005_SEEPTYPE(id) ((id) & 0x0C0) >> 6)
+#define SUBID_9005_SEEPTYPE_NONE 0x0
+#define SUBID_9005_SEEPTYPE_4K 0x1
+
+static ahd_device_setup_t ahd_aic7901_setup;
+static ahd_device_setup_t ahd_aic7902_setup;
+
+struct ahd_pci_identity ahd_pci_ident_table [] =
+{
+ /* aic7901 based controllers */
+ {
+ ID_AHA_29320,
+ ID_ALL_MASK,
+ "Adaptec 29320 Ultra320 SCSI adapter",
+ ahd_aic7901_setup
+ },
+ {
+ ID_AHA_29320LP,
+ ID_ALL_MASK,
+ "Adaptec 29320LP Ultra320 SCSI adapter",
+ ahd_aic7901_setup
+ },
+ /* aic7902 based controllers */
+ {
+ ID_AHA_39320,
+ ID_ALL_MASK,
+ "Adaptec 39320 Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320D,
+ ID_ALL_MASK,
+ "Adaptec 39320D Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320D_CPQ,
+ ID_ALL_MASK,
+ "Adaptec (Compaq OEM) 39320D Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ /* Generic chip probes for devices we don't know 'exactly' */
+ {
+ ID_AIC7901 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7901 Ultra320 SCSI adapter",
+ ahd_aic7901_setup
+ },
+ {
+ ID_AIC7902 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7902 Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ }
+};
+
+const u_int ahd_num_pci_devs = NUM_ELEMENTS(ahd_pci_ident_table);
+
+#define DEVCONFIG 0x40
+#define PCIXINITPAT 0x0000E000ul
+#define PCIXINIT_PCI33_66 0x0000E000ul
+#define PCIXINIT_PCIX50_66 0x0000C000ul
+#define PCIXINIT_PCIX66_100 0x0000A000ul
+#define PCIXINIT_PCIX100_133 0x00008000ul
+#define PCI_BUS_MODES_INDEX(devconfig) \
+ (((devconfig) & PCIXINITPAT) >> 13)
+static const char *pci_bus_modes[] =
+{
+ "PCI bus mode unknown",
+ "PCI bus mode unknown",
+ "PCI bus mode unknown",
+ "PCI bus mode unknown",
+ "PCI-X 101-133Mhz",
+ "PCI-X 67-100Mhz",
+ "PCI-X 50-66Mhz",
+ "PCI 33 or 66Mhz"
+};
+
+#define TESTMODE 0x00000800ul
+#define IRDY_RST 0x00000200ul
+#define FRAME_RST 0x00000100ul
+#define PCI64BIT 0x00000080ul
+#define MRDCEN 0x00000040ul
+#define ENDIANSEL 0x00000020ul
+#define MIXQWENDIANEN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define QWENDIANSEL 0x00000001ul
+
+#define DEVCONFIG1 0x44
+#define PREQDIS 0x01
+
+#define CSIZE_LATTIME 0x0c
+#define CACHESIZE 0x000000fful
+#define LATTIME 0x0000ff00ul
+
+static int ahd_check_extport(struct ahd_softc *ahd);
+static void ahd_configure_termination(struct ahd_softc *ahd,
+ u_int adapter_control);
+static void ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat);
+
+struct ahd_pci_identity *
+ahd_find_pci_device(ahd_dev_softc_t pci)
+{
+ uint64_t full_id;
+ uint16_t device;
+ uint16_t vendor;
+ uint16_t subdevice;
+ uint16_t subvendor;
+ struct ahd_pci_identity *entry;
+ u_int i;
+
+ vendor = ahd_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+ device = ahd_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+ subvendor = ahd_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+ subdevice = ahd_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+ full_id = ahd_compose_id(device,
+ vendor,
+ subdevice,
+ subvendor);
+
+ for (i = 0; i < ahd_num_pci_devs; i++) {
+ entry = &ahd_pci_ident_table[i];
+ if (entry->full_id == (full_id & entry->id_mask)) {
+ /* Honor exclusion entries. */
+ if (entry->name == NULL)
+ return (NULL);
+ return (entry);
+ }
+ }
+ return (NULL);
+}
+
+int
+ahd_pci_config(struct ahd_softc *ahd, struct ahd_pci_identity *entry)
+{
+ struct scb_data *shared_scb_data;
+ u_long l;
+ u_long s;
+ u_int command;
+ uint32_t devconfig;
+ uint16_t subvendor;
+ int error;
+
+ shared_scb_data = NULL;
+ error = entry->setup(ahd);
+ if (error != 0)
+ return (error);
+
+ ahd->description = entry->name;
+ devconfig = ahd_pci_read_config(ahd->dev_softc, DEVCONFIG, /*bytes*/4);
+ if ((devconfig & PCIXINITPAT) == PCIXINIT_PCI33_66) {
+ ahd->chip |= AHD_PCI;
+ /* Disable PCIX workarounds when running in PCI mode. */
+ ahd->bugs &= ~AHD_PCIX_BUG_MASK;
+ } else {
+ ahd->chip |= AHD_PCIX;
+ }
+ ahd->bus_description = pci_bus_modes[PCI_BUS_MODES_INDEX(devconfig)];
+
+ /*
+ * Record if this is a Compaq board.
+ */
+ subvendor = ahd_pci_read_config(ahd->dev_softc,
+ PCIR_SUBVEND_0, /*bytes*/2);
+ if (subvendor == SUBID_CPQ)
+ ahd->flags |= AHD_CPQ_BOARD;
+
+ ahd_power_state_change(ahd, AHD_POWER_STATE_D0);
+
+ error = ahd_pci_map_registers(ahd);
+ if (error != 0)
+ return (error);
+
+ /*
+ * If we need to support high memory, enable dual
+ * address cycles. This bit must be set to enable
+ * high address bit generation even if we are on a
+ * 64bit bus (PCI64BIT set in devconfig).
+ */
+ if ((ahd->flags & (AHD_39BIT_ADDRESSING|AHD_64BIT_ADDRESSING)) != 0) {
+ uint32_t devconfig;
+
+ if (bootverbose)
+ printf("%s: Enabling 39Bit Addressing\n",
+ ahd_name(ahd));
+ devconfig = ahd_pci_read_config(ahd->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ devconfig |= DACEN;
+ ahd_pci_write_config(ahd->dev_softc, DEVCONFIG,
+ devconfig, /*bytes*/4);
+ }
+
+ /* Ensure busmastering is enabled */
+ command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/1);
+ command |= PCIM_CMD_BUSMASTEREN;
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, /*bytes*/1);
+
+ error = ahd_softc_init(ahd);
+ if (error != 0)
+ return (error);
+
+ ahd->bus_intr = ahd_pci_intr;
+
+ error = ahd_reset(ahd);
+ if (error != 0)
+ return (ENXIO);
+
+ ahd->pci_cachesize =
+ ahd_pci_read_config(ahd->dev_softc, CSIZE_LATTIME,
+ /*bytes*/1) & CACHESIZE;
+ ahd->pci_cachesize *= 4;
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ /* See if we have a SEEPROM and perform auto-term */
+ error = ahd_check_extport(ahd);
+ if (error != 0)
+ return (error);
+
+ /* Core initialization */
+ error = ahd_init(ahd);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Allow interrupts now that we are completely setup.
+ */
+ error = ahd_pci_map_int(ahd);
+ if (error != 0)
+ return (error);
+
+ ahd_list_lock(&l);
+ /*
+ * Link this softc in with all other ahd instances.
+ */
+ ahd_softc_insert(ahd);
+
+ ahd_lock(ahd, &s);
+ ahd_intr_enable(ahd, TRUE);
+ ahd_unlock(ahd, &s);
+
+ ahd_list_unlock(&l);
+ return (0);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static int
+ahd_check_extport(struct ahd_softc *ahd)
+{
+ struct seeprom_config *sc;
+ u_int adapter_control;
+ int have_seeprom;
+ int error;
+
+ sc = ahd->seep_config;
+ have_seeprom = ahd_acquire_seeprom(ahd);
+ if (have_seeprom) {
+ u_int start_addr;
+
+ if (bootverbose)
+ printf("%s: Reading SEEPROM...", ahd_name(ahd));
+
+ /* Address is always in units of 16bit words */
+ start_addr = (sizeof(*sc) / 2) * (ahd->channel - 'A');
+
+ error = ahd_read_seeprom(ahd, (uint16_t *)sc,
+ start_addr, sizeof(*sc)/2);
+
+ if (error != 0) {
+ printf("Unable to read SEEPROM\n");
+ have_seeprom = 0;
+ } else {
+ have_seeprom = ahd_verify_cksum(sc);
+
+ if (bootverbose) {
+ if (have_seeprom == 0)
+ printf ("checksum error\n");
+ else
+ printf ("done.\n");
+ }
+ }
+ ahd_release_seeprom(ahd);
+ }
+
+ if (!have_seeprom) {
+ u_int nvram_scb;
+
+ /*
+ * Pull scratch ram settings and treat them as
+ * if they are the contents of an seeprom if
+ * the 'ADPT', 'BIOS', or 'ASPI' signature is found
+ * in SCB 0xFF. We manually compose the data as 16bit
+ * values to avoid endian issues.
+ */
+ ahd_set_scbptr(ahd, 0xFF);
+ nvram_scb = ahd_inb_scbram(ahd, SCB_BASE + NVRAM_SCB_OFFSET);
+ printf("nvram_scb == 0x%x\n", nvram_scb);
+ printf("SCBPTR == 0x%x\n", ahd_get_scbptr(ahd));
+ printf("Signature = %c%c%c%c\n",
+ ahd_inb_scbram(ahd, SCB_BASE + 0),
+ ahd_inb_scbram(ahd, SCB_BASE + 1),
+ ahd_inb_scbram(ahd, SCB_BASE + 2),
+ ahd_inb_scbram(ahd, SCB_BASE + 3));
+ if (nvram_scb != 0xFF
+ && ((ahd_inb_scbram(ahd, SCB_BASE + 0) == 'A'
+ && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'D'
+ && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'P'
+ && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'T')
+ || (ahd_inb_scbram(ahd, SCB_BASE + 0) == 'B'
+ && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'I'
+ && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'O'
+ && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'S')
+ || (ahd_inb_scbram(ahd, SCB_BASE + 0) == 'A'
+ && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'S'
+ && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'P'
+ && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'I'))) {
+ uint16_t *sc_data;
+ int i;
+
+ ahd_set_scbptr(ahd, nvram_scb);
+ sc_data = (uint16_t *)sc;
+ for (i = 0; i < 64; i += 2)
+ *sc_data++ = ahd_inw_scbram(ahd, SCB_BASE+i);
+ have_seeprom = ahd_verify_cksum(sc);
+ if (have_seeprom)
+ ahd->flags |= AHD_SCB_CONFIG_USED;
+ }
+ }
+
+#ifdef AHD_DEBUG
+ if (have_seeprom != 0
+ && (ahd_debug & AHD_DUMP_SEEPROM) != 0) {
+ uint8_t *sc_data;
+ int i;
+
+ printf("%s: Seeprom Contents:", ahd_name(ahd));
+ sc_data = (uint8_t *)sc;
+ for (i = 0; i < (sizeof(*sc)); i += 2)
+ printf("\n\t0x%.4x",
+ sc_data[i] | (sc_data[i+1] << 8));
+ printf("\n");
+ }
+#endif
+
+ if (!have_seeprom) {
+ if (bootverbose)
+ printf("%s: No SEEPROM available.\n", ahd_name(ahd));
+ ahd->flags |= AHD_USEDEFAULTS;
+ error = ahd_default_config(ahd);
+ adapter_control = CFAUTOTERM|CFSEAUTOTERM;
+ free(ahd->seep_config, M_DEVBUF);
+ ahd->seep_config = NULL;
+ } else {
+ error = ahd_parse_cfgdata(ahd, sc);
+ adapter_control = sc->adapter_control;
+ }
+ if (error != 0)
+ return (error);
+
+ ahd_configure_termination(ahd, adapter_control);
+
+ return (0);
+}
+
+static void
+ahd_configure_termination(struct ahd_softc *ahd, u_int adapter_control)
+{
+ int error;
+ u_int sxfrctl1;
+ uint8_t termctl;
+ uint32_t devconfig;
+
+ devconfig = ahd_pci_read_config(ahd->dev_softc, DEVCONFIG, /*bytes*/4);
+ devconfig &= ~STPWLEVEL;
+ if ((ahd->flags & AHD_STPWLEVEL_A) != 0) {
+ devconfig |= STPWLEVEL;
+ }
+ ahd_pci_write_config(ahd->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+
+ /* Make sure current sensing is off. */
+ if ((ahd->flags & AHD_CURRENT_SENSING) != 0) {
+ (void)ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, 0);
+ }
+
+ /*
+ * Read to sense. Write to set.
+ */
+ error = ahd_read_flexport(ahd, FLXADDR_TERMCTL, &termctl);
+ if ((adapter_control & CFAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual Primary Termination\n",
+ ahd_name(ahd));
+ termctl &= ~(FLX_TERMCTL_ENPRILOW|FLX_TERMCTL_ENPRIHIGH);
+ if ((adapter_control & CFSTERM) != 0)
+ termctl |= FLX_TERMCTL_ENPRILOW;
+ if ((adapter_control & CFWSTERM) != 0)
+ termctl |= FLX_TERMCTL_ENPRIHIGH;
+ } else if (error != 0) {
+ printf("%s: Primary Auto-Term Sensing failed! "
+ "Using Defaults.\n", ahd_name(ahd));
+ termctl = FLX_TERMCTL_ENPRILOW|FLX_TERMCTL_ENPRIHIGH;
+ }
+
+ if ((adapter_control & CFSEAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual Secondary Termination\n",
+ ahd_name(ahd));
+ termctl &= ~(FLX_TERMCTL_ENSECLOW|FLX_TERMCTL_ENSECHIGH);
+ if ((adapter_control & CFSELOWTERM) != 0)
+ termctl |= FLX_TERMCTL_ENSECLOW;
+ if ((adapter_control & CFSEHIGHTERM) != 0)
+ termctl |= FLX_TERMCTL_ENSECHIGH;
+ } else if (error != 0) {
+ printf("%s: Secondary Auto-Term Sensing failed! "
+ "Using Defaults.\n", ahd_name(ahd));
+ termctl |= FLX_TERMCTL_ENSECLOW|FLX_TERMCTL_ENSECHIGH;
+ }
+
+ /*
+ * Now set the termination based on what we found.
+ */
+ sxfrctl1 = ahd_inb(ahd, SXFRCTL1) & ~STPWEN;
+ if ((termctl & FLX_TERMCTL_ENPRILOW) != 0) {
+ ahd->flags |= AHD_TERM_ENB_A;
+ sxfrctl1 |= STPWEN;
+ }
+ /* Must set the latch once in order to be effective. */
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1|STPWEN);
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1);
+
+ error = ahd_write_flexport(ahd, FLXADDR_TERMCTL, termctl);
+ if (error != 0) {
+ printf("%s: Unable to set termination settings!\n",
+ ahd_name(ahd));
+ } else if (bootverbose) {
+ printf("%s: Primary High byte termination %sabled\n",
+ ahd_name(ahd),
+ (termctl & FLX_TERMCTL_ENPRIHIGH) ? "En" : "Dis");
+
+ printf("%s: Primary Low byte termination %sabled\n",
+ ahd_name(ahd),
+ (termctl & FLX_TERMCTL_ENPRILOW) ? "En" : "Dis");
+
+ printf("%s: Secondary High byte termination %sabled\n",
+ ahd_name(ahd),
+ (termctl & FLX_TERMCTL_ENSECHIGH) ? "En" : "Dis");
+
+ printf("%s: Secondary Low byte termination %sabled\n",
+ ahd_name(ahd),
+ (termctl & FLX_TERMCTL_ENSECLOW) ? "En" : "Dis");
+ }
+ return;
+}
+
+#define DPE 0x80
+#define SSE 0x40
+#define RMA 0x20
+#define RTA 0x10
+#define STA 0x08
+#define DPR 0x01
+
+static const char *split_status_source[] =
+{
+ "DFF0",
+ "DFF1",
+ "OVLY",
+ "CMC",
+};
+
+static const char *pci_status_source[] =
+{
+ "DFF0",
+ "DFF1",
+ "SG",
+ "CMC",
+ "OVLY",
+ "NONE",
+ "MSI",
+ "TARG"
+};
+
+static const char *split_status_strings[] =
+{
+ "%s: Received split response in %s.\n"
+ "%s: Received split completion error message in %s\n",
+ "%s: Receive overrun in %s\n",
+ "%s: Count not complete in %s\n",
+ "%s: Split completion data bucket in %s\n",
+ "%s: Split completion address error in %s\n",
+ "%s: Split completion byte count error in %s\n",
+ "%s: Signaled Target-abort to early terminate a split in %s\n",
+};
+
+static const char *pci_status_strings[] =
+{
+ "%s: Data Parity Error has been reported via PERR# in %s\n",
+ "%s: Target initial wait state error in %s\n",
+ "%s: Split completion read data parity error in %s\n",
+ "%s: Split completion address attribute parity error in %s\n",
+ "%s: Received a Target Abort in %s\n",
+ "%s: Received a Master Abort in %s\n",
+ "%s: Signal System Error Detected in %s\n",
+ "%s: Address or Write Phase Parity Error Detected in %s.\n"
+};
+
+void
+ahd_pci_intr(struct ahd_softc *ahd)
+{
+ uint8_t pci_status[8];
+ ahd_mode_state saved_modes;
+ u_int pci_status1;
+ u_int intstat;
+ u_int i;
+ u_int reg;
+
+ intstat = ahd_inb(ahd, INTSTAT);
+
+ if ((intstat & SPLTINT) != 0)
+ ahd_pci_split_intr(ahd, intstat);
+
+ if ((intstat & PCIINT) == 0)
+ return;
+
+ printf("%s: PCI error Interrupt\n", ahd_name(ahd));
+ saved_modes = ahd_save_modes(ahd);
+ ahd_dump_card_state(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ for (i = 0, reg = DF0PCISTAT; i < 8; i++, reg++) {
+
+ if (i == 5)
+ continue;
+ pci_status[i] = ahd_inb(ahd, reg);
+ /* Clear latched errors. So our interupt deasserts. */
+ ahd_outb(ahd, reg, pci_status[i]);
+ }
+
+ for (i = 0; i < 8; i++) {
+ u_int bit;
+
+ if (i == 5)
+ continue;
+
+ for (bit = 0; bit < 8; bit++) {
+
+ if ((pci_status[i] & (0x1 << bit)) != 0) {
+ static const char *s;
+
+ s = pci_status_strings[bit];
+ if (i == 7/*TARG*/ && bit == 3)
+ s = "%s: Signal Target Abort\n";
+ printf(s, ahd_name(ahd), pci_status_source[i]);
+ }
+ }
+ }
+ pci_status1 = ahd_pci_read_config(ahd->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+ pci_status1, /*bytes*/1);
+ ahd_restore_modes(ahd, saved_modes);
+ ahd_unpause(ahd);
+}
+
+static void
+ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat)
+{
+ uint8_t split_status[4];
+ uint8_t split_status1[4];
+ uint8_t sg_split_status[2];
+ uint8_t sg_split_status1[2];
+ ahd_mode_state saved_modes;
+ u_int i;
+ uint16_t pcix_status;
+
+ /*
+ * Check for splits in all modes. Modes 0 and 1
+ * additionally have SG engine splits to look at.
+ */
+ pcix_status = ahd_pci_read_config(ahd->dev_softc, PCIXR_STATUS,
+ /*bytes*/2);
+ printf("%s: PCI Split Interrupt - PCI-X status = 0x%x\n",
+ ahd_name(ahd), pcix_status);
+ saved_modes = ahd_save_modes(ahd);
+ for (i = 0; i < 4; i++) {
+ ahd_set_modes(ahd, i, i);
+
+ split_status[i] = ahd_inb(ahd, DCHSPLTSTAT0);
+ split_status1[i] = ahd_inb(ahd, DCHSPLTSTAT1);
+ /* Clear latched errors. So our interupt deasserts. */
+ ahd_outb(ahd, DCHSPLTSTAT0, split_status[i]);
+ ahd_outb(ahd, DCHSPLTSTAT1, split_status1[i]);
+ if (i != 0)
+ continue;
+ sg_split_status[i] = ahd_inb(ahd, SGSPLTSTAT0);
+ sg_split_status1[i] = ahd_inb(ahd, SGSPLTSTAT1);
+ /* Clear latched errors. So our interupt deasserts. */
+ ahd_outb(ahd, SGSPLTSTAT0, sg_split_status[i]);
+ ahd_outb(ahd, SGSPLTSTAT1, sg_split_status1[i]);
+ }
+
+ for (i = 0; i < 4; i++) {
+ u_int bit;
+
+ for (bit = 0; bit < 8; bit++) {
+
+ if ((split_status[i] & (0x1 << bit)) != 0) {
+ static const char *s;
+
+ s = split_status_strings[bit];
+ printf(s, ahd_name(ahd),
+ split_status_source[i]);
+ }
+
+ if (i != 0)
+ continue;
+
+ if ((sg_split_status[i] & (0x1 << bit)) != 0) {
+ static const char *s;
+
+ s = split_status_strings[bit];
+ printf(s, ahd_name(ahd), "SG");
+ }
+ }
+ }
+ /*
+ * Clear PCI-X status bits.
+ */
+ ahd_pci_write_config(ahd->dev_softc, PCIXR_STATUS,
+ pcix_status, /*bytes*/2);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+static int
+ahd_aic7901_setup(struct ahd_softc *ahd)
+{
+ ahd_dev_softc_t pci;
+
+ pci = ahd->dev_softc;
+ ahd->channel = 'A';
+ ahd->chip = AHD_AIC7901;
+ ahd->features = AHD_AIC7901_FE;
+ return (0);
+}
+
+static int
+ahd_aic7902_setup(struct ahd_softc *ahd)
+{
+ ahd_dev_softc_t pci;
+ u_int rev;
+ u_int devconfig1;
+
+ pci = ahd->dev_softc;
+ rev = ahd_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev < ID_AIC7902_PCI_REV_A3) {
+ printf("%s: Unable to attach to unsupported chip revision %d\n",
+ ahd_name(ahd), rev);
+ ahd_pci_write_config(pci, PCIR_COMMAND, 0, /*bytes*/1);
+ return (ENXIO);
+ }
+ if (rev < ID_AIC7902_PCI_REV_B0) {
+ /*
+ * Pending request assertion does not work on the A if we have
+ * DMA requests outstanding on both channels. See H2A3 Razors
+ * #327 and #365.
+ */
+ devconfig1 = ahd_pci_read_config(pci, DEVCONFIG1, /*bytes*/1);
+ ahd_pci_write_config(pci, DEVCONFIG1,
+ devconfig1|PREQDIS, /*bytes*/1);
+ devconfig1 = ahd_pci_read_config(pci, DEVCONFIG1, /*bytes*/1);
+ /*
+ * Enable A series workarounds.
+ */
+ ahd->bugs |= AHD_SENT_SCB_UPDATE_BUG|AHD_ABORT_LQI_BUG
+ | AHD_PKT_BITBUCKET_BUG|AHD_LONG_SETIMO_BUG
+ | AHD_NLQICRC_DELAYED_BUG|AHD_SCSIRST_BUG
+ | AHD_LQO_ATNO_BUG|AHD_AUTOFLUSH_BUG
+ | AHD_CLRLQO_AUTOCLR_BUG|AHD_PCIX_MMAPIO_BUG
+ | AHD_PCIX_CHIPRST_BUG|AHD_PKTIZED_STATUS_BUG;
+ if (rev < ID_AIC7902_PCI_REV_A4)
+ ahd->bugs |= AHD_PCIX_ARBITER_BUG|AHD_PCIX_SPLIT_BUG;
+ }
+
+ ahd->channel = ahd_get_pci_function(pci) + 'A';
+ ahd->chip = AHD_AIC7902;
+ ahd->features = AHD_AIC7902_FE;
+ return (0);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_proc.c b/xen/drivers/scsi/aic7xxx/aic79xx_proc.c
new file mode 100644
index 0000000000..a9b05d4fe6
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_proc.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
+ * sym driver.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_proc.c#7 $
+ */
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+
+static void copy_mem_info(struct info_str *info, char *data, int len);
+static int copy_info(struct info_str *info, char *fmt, ...);
+static u_int scsi_calc_syncsrate(u_int period_factor);
+static void ahd_dump_target_state(struct ahd_softc *ahd,
+ struct info_str *info,
+ u_int our_id, char channel,
+ u_int target_id, u_int target_offset);
+static void ahd_dump_device_state(struct info_str *info,
+ struct ahd_linux_device *dev);
+static int ahd_proc_write_seeprom(struct ahd_softc *ahd,
+ char *buffer, int length);
+
+static void
+copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->offset + info->length)
+ len = info->offset + info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+
+ if (info->pos < info->offset) {
+ off_t partial;
+
+ partial = info->offset - info->pos;
+ data += partial;
+ info->pos += partial;
+ len -= partial;
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer, data, len);
+ info->pos += len;
+ info->buffer += len;
+ }
+}
+
+static int
+copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[256];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return (len);
+}
+
+/*
+ * Table of syncrates that don't follow the "divisible by 4"
+ * rule. This table will be expanded in future SCSI specs.
+ */
+static struct {
+ u_int period_factor;
+ u_int period; /* in 100ths of ns */
+} scsi_syncrates[] = {
+ { 0x08, 625 }, /* FAST-160 */
+ { 0x09, 1250 }, /* FAST-80 */
+ { 0x0a, 2500 }, /* FAST-40 40MHz */
+ { 0x0b, 3030 }, /* FAST-40 33MHz */
+ { 0x0c, 5000 } /* FAST-20 */
+};
+
+/*
+ * Return the frequency in kHz corresponding to the given
+ * sync period factor.
+ */
+static u_int
+scsi_calc_syncsrate(u_int period_factor)
+{
+ int i;
+ int num_syncrates;
+
+ num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]);
+ /* See if the period is in the "exception" table */
+ for (i = 0; i < num_syncrates; i++) {
+
+ if (period_factor == scsi_syncrates[i].period_factor) {
+ /* Period in kHz */
+ return (100000000 / scsi_syncrates[i].period);
+ }
+ }
+
+ /*
+ * Wasn't in the table, so use the standard
+ * 4 times conversion.
+ */
+ return (10000000 / (period_factor * 4 * 10));
+}
+
+void
+ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo)
+{
+ u_int speed;
+ u_int freq;
+ u_int mb;
+
+ speed = 3300;
+ freq = 0;
+ if (tinfo->offset != 0) {
+ freq = scsi_calc_syncsrate(tinfo->period);
+ speed = freq;
+ }
+ speed *= (0x01 << tinfo->width);
+ mb = speed / 1000;
+ if (mb > 0)
+ copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
+ else
+ copy_info(info, "%dKB/s transfers", speed);
+
+ if (freq != 0) {
+ copy_info(info, " (%d.%03dMHz%s, offset %d",
+ freq / 1000, freq % 1000,
+ (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+ ? " DT" : "", tinfo->offset);
+ }
+
+ if (tinfo->width > 0) {
+ if (freq != 0) {
+ copy_info(info, ", ");
+ } else {
+ copy_info(info, " (");
+ }
+ copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+ } else if (freq != 0) {
+ copy_info(info, ")");
+ }
+ copy_info(info, "\n");
+}
+
+static void
+ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
+ u_int our_id, char channel, u_int target_id,
+ u_int target_offset)
+{
+ struct ahd_linux_target *targ;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ int lun;
+
+ tinfo = ahd_fetch_transinfo(ahd, channel, our_id,
+ target_id, &tstate);
+ copy_info(info, "Channel %c Target %d Negotiation Settings\n",
+ channel, target_id);
+ copy_info(info, "\tUser: ");
+ ahd_format_transinfo(info, &tinfo->user);
+ targ = ahd->platform_data->targets[target_offset];
+ if (targ == NULL)
+ return;
+
+ copy_info(info, "\tGoal: ");
+ ahd_format_transinfo(info, &tinfo->goal);
+ copy_info(info, "\tCurr: ");
+ ahd_format_transinfo(info, &tinfo->curr);
+
+ for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+ struct ahd_linux_device *dev;
+
+ dev = targ->devices[lun];
+
+ if (dev == NULL)
+ continue;
+
+ ahd_dump_device_state(info, dev);
+ }
+}
+
+static void
+ahd_dump_device_state(struct info_str *info, struct ahd_linux_device *dev)
+{
+ copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+ dev->target->channel + 'A', dev->target->target, dev->lun);
+
+ copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued);
+ copy_info(info, "\t\tCommands Active %d\n", dev->active);
+ copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
+ copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+ copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+}
+
+static int
+ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length)
+{
+ ahd_mode_state saved_modes;
+ int have_seeprom;
+ u_long s;
+ int paused;
+ int written;
+
+ /* Default to failure. */
+ written = -EINVAL;
+ ahd_lock(ahd, &s);
+ paused = ahd_is_paused(ahd);
+ if (!paused)
+ ahd_pause(ahd);
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ if (length != sizeof(struct seeprom_config)) {
+ printf("ahd_proc_write_seeprom: incorrect buffer size\n");
+ goto done;
+ }
+
+ have_seeprom = ahd_verify_cksum((struct seeprom_config*)buffer);
+ if (have_seeprom == 0) {
+ printf("ahd_proc_write_seeprom: cksum verification failed\n");
+ goto done;
+ }
+
+ have_seeprom = ahd_acquire_seeprom(ahd);
+ if (!have_seeprom) {
+ printf("ahd_proc_write_seeprom: No Serial EEPROM\n");
+ goto done;
+ } else {
+ u_int start_addr;
+
+ if (ahd->seep_config == NULL) {
+ ahd->seep_config = malloc(sizeof(*ahd->seep_config),
+ M_DEVBUF, M_NOWAIT);
+ if (ahd->seep_config == NULL) {
+ printf("aic79xx: Unable to allocate serial "
+ "eeprom buffer. Write failing\n");
+ goto done;
+ }
+ }
+ printf("aic79xx: Writing Serial EEPROM\n");
+ start_addr = 32 * (ahd->channel - 'A');
+ ahd_write_seeprom(ahd, (u_int16_t *)buffer, start_addr,
+ sizeof(struct seeprom_config)/2);
+ ahd_read_seeprom(ahd, (uint16_t *)ahd->seep_config,
+ start_addr, sizeof(struct seeprom_config)/2);
+ ahd_release_seeprom(ahd);
+ written = length;
+ }
+
+done:
+ ahd_restore_modes(ahd, saved_modes);
+ if (!paused)
+ ahd_unpause(ahd);
+ ahd_unlock(ahd, &s);
+ return (written);
+}
+/*
+ * Return information to handle /proc support for the driver.
+ */
+int
+ahd_linux_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ struct ahd_softc *ahd;
+ struct info_str info;
+ char ahd_info[256];
+ u_long l;
+ u_int max_targ;
+ u_int i;
+ int retval;
+
+ retval = -EINVAL;
+ ahd_list_lock(&l);
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+ if (ahd->platform_data->host->host_no == hostno)
+ break;
+ }
+
+ if (ahd == NULL)
+ goto done;
+
+ /* Has data been written to the file? */
+ if (inout == TRUE) {
+ retval = ahd_proc_write_seeprom(ahd, buffer, length);
+ goto done;
+ }
+
+ if (start)
+ *start = buffer;
+
+ info.buffer = buffer;
+ info.length = length;
+ info.offset = offset;
+ info.pos = 0;
+
+ copy_info(&info, "Adaptec AIC79xx driver version: %s\n",
+ AIC79XX_DRIVER_VERSION);
+ ahd_controller_info(ahd, ahd_info);
+ copy_info(&info, "%s\n\n", ahd_info);
+
+ if (ahd->seep_config == NULL)
+ copy_info(&info, "No Serial EEPROM\n");
+ else {
+ copy_info(&info, "Serial EEPROM:\n");
+ for (i = 0; i < sizeof(*ahd->seep_config)/2; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ copy_info(&info, "\n");
+ }
+ copy_info(&info, "0x%.4x ",
+ ((uint16_t*)ahd->seep_config)[i]);
+ }
+ copy_info(&info, "\n");
+ }
+ copy_info(&info, "\n");
+
+ max_targ = 15;
+ if ((ahd->features & AHD_WIDE) == 0)
+ max_targ = 7;
+
+ for (i = 0; i <= max_targ; i++) {
+
+ ahd_dump_target_state(ahd, &info, ahd->our_id, 'A',
+ /*target_id*/i, /*target_offset*/i);
+ }
+ retval = info.pos > info.offset ? info.pos - info.offset : 0;
+done:
+ ahd_list_unlock(&l);
+ return (retval);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_reg.h b/xen/drivers/scsi/aic7xxx/aic79xx_reg.h
new file mode 100644
index 0000000000..45852b5221
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_reg.h
@@ -0,0 +1,1357 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#42 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#34 $
+ */
+
+#define MODE_PTR 0x00
+#define DST_MODE 0x70
+#define SRC_MODE 0x07
+
+#define INTSTAT 0x01
+#define INT_PEND 0xff
+#define HWERRINT 0x80
+#define BRKADRINT 0x40
+#define SWTMINT 0x20
+#define PCIINT 0x10
+#define SCSIINT 0x08
+#define SEQINT 0x04
+#define CMDCMPLT 0x02
+#define SPLTINT 0x01
+
+#define SEQINTCODE 0x02
+#define PCIX_ARBITOR_WW 0x19
+#define ENTERING_NONPACK 0x18
+#define MONITORDRAIN 0x17
+#define SNAPSHOTCLRCHN 0x16
+#define CFG4OVERRUN 0x15
+#define STATUS_OVERRUN 0x14
+#define CFG4ISTAT_INTR 0x13
+#define INVALID_SEQINT 0x12
+#define ILLEGAL_PHASE 0x11
+#define DUMP_CARD_STATE 0x10
+#define NO_FREE_FIFO 0x0f
+#define OUT_OF_RANGE 0x0e
+#define NO_FREE_SCB 0x0d
+#define SCB_MISMATCH 0x0c
+#define MISSED_BUSFREE 0x0b
+#define MKMSG_FAILED 0x0a
+#define DATA_OVERRUN 0x09
+#define BAD_STATUS 0x08
+#define HOST_MSG_LOOP 0x07
+#define PDATA_REINIT 0x06
+#define IGN_WIDE_RES 0x05
+#define NO_MATCH 0x04
+#define PROTO_VIOLATION 0x03
+#define SEND_REJECT 0x02
+#define BAD_PHASE 0x01
+
+#define CLRINT 0x03
+#define CLRBRKADRINT 0x40
+#define CLRSWTMINT 0x20
+#define CLRSCSIINT 0x08
+#define CLRSEQINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSPLTINT 0x01
+
+#define ERROR 0x04
+#define CIOPARERR 0x80
+#define MPARERR 0x20
+#define DPARERR 0x10
+#define SQPARERR 0x08
+#define ILLOPCODE 0x04
+#define DSCTMOUT 0x02
+
+#define CLRERR 0x04
+#define CLRCIOPARERR 0x80
+#define CLRMPARERR 0x20
+#define CLRDPARERR 0x10
+#define CLRSQPARERR 0x08
+#define CLRILLOPCODE 0x04
+#define CLRDSCTMOUT 0x02
+
+#define HCNTRL 0x05
+#define POWRDN 0x40
+#define SWINT 0x10
+#define HCNTRL3 0x08
+#define PAUSE 0x04
+#define INTEN 0x02
+#define CHIPRST 0x01
+#define CHIPRSTACK 0x01
+
+#define HNSCB_QOFF 0x06
+
+#define HESCB_QOFF 0x08
+
+#define HS_MAILBOX 0x0b
+#define HOST_TQINPOS 0x80
+
+#define SEQINTSTAT 0x0c
+#define SEQ_SWTMRTO 0x10
+#define SEQ_SEQINT 0x08
+#define SEQ_SCSIINT 0x04
+#define SEQ_PCIINT 0x02
+#define SEQ_SPLTINT 0x01
+
+#define SNSCB_QOFF 0x10
+
+#define SESCB_QOFF 0x12
+
+#define SDSCB_QOFF 0x14
+
+#define QOFF_CTLSTA 0x16
+#define EMPTY_SCB_AVAIL 0x80
+#define NEW_SCB_AVAIL 0x40
+#define SDSCB_ROLLOVR 0x20
+#define HS_MAILBOX_ACT 0x10
+#define SCB_QSIZE 0x0f
+#define SCB_QSIZE_16384 0x0c
+#define SCB_QSIZE_8192 0x0b
+#define SCB_QSIZE_4096 0x0a
+#define SCB_QSIZE_2048 0x09
+#define SCB_QSIZE_1024 0x08
+#define SCB_QSIZE_512 0x07
+#define SCB_QSIZE_256 0x06
+#define SCB_QSIZE_128 0x05
+#define SCB_QSIZE_64 0x04
+#define SCB_QSIZE_32 0x03
+#define SCB_QSIZE_16 0x02
+#define SCB_QSIZE_8 0x01
+#define SCB_QSIZE_4 0x00
+
+#define INTCTL 0x18
+#define SWTMINTMASK 0x80
+#define SWTMINTEN 0x40
+#define SWTIMER_START 0x20
+#define AUTOCLRCMDINT 0x10
+#define PCIINTEN 0x08
+#define SCSIINTEN 0x04
+#define SEQINTEN 0x02
+#define SPLTINTEN 0x01
+
+#define DFCNTRL 0x19
+#define SCSIENACK 0x20
+#define DIRECTIONACK 0x04
+#define FIFOFLUSHACK 0x02
+#define DIRECTIONEN 0x01
+
+#define DSCOMMAND0 0x19
+#define CACHETHEN 0x80
+#define DPARCKEN 0x40
+#define MPARCKEN 0x20
+#define EXTREQLCK 0x10
+#define CIOPARCKEN 0x01
+
+#define DFSTATUS 0x1a
+#define PRELOAD_AVAIL 0x80
+#define PKT_PRELOAD_AVAIL 0x40
+#define MREQPEND 0x10
+#define HDONE 0x08
+#define DFTHRESH 0x04
+#define FIFOFULL 0x02
+#define FIFOEMP 0x01
+
+#define SG_CACHE_SHADOW 0x1b
+#define ODD_SEG 0x04
+#define LAST_SEG 0x02
+#define LAST_SEG_DONE 0x01
+
+#define ARBCTL 0x1b
+#define RESET_HARB 0x80
+#define RETRY_SWEN 0x08
+#define USE_TIME 0x07
+
+#define SG_CACHE_PRE 0x1b
+
+#define LQIN 0x20
+
+#define TYPEPTR 0x20
+
+#define TAGPTR 0x21
+
+#define LUNPTR 0x22
+
+#define DATALENPTR 0x23
+
+#define STATLENPTR 0x24
+
+#define CMDLENPTR 0x25
+
+#define ATTRPTR 0x26
+
+#define FLAGPTR 0x27
+
+#define CMDPTR 0x28
+
+#define QNEXTPTR 0x29
+
+#define IDPTR 0x2a
+
+#define ABRTBYTEPTR 0x2b
+
+#define ABRTBITPTR 0x2c
+
+#define LUNLEN 0x30
+
+#define CDBLIMIT 0x31
+
+#define MAXCMD 0x32
+
+#define MAXCMDCNT 0x33
+
+#define LQRSVD01 0x34
+
+#define LQRSVD16 0x35
+
+#define LQRSVD17 0x36
+
+#define CMDRSVD0 0x37
+
+#define LQCTL0 0x38
+#define LQITARGCLT 0xc0
+#define LQIINITGCLT 0x30
+#define LQ0TARGCLT 0x0c
+#define LQ0INITGCLT 0x03
+
+#define LQCTL1 0x38
+#define PCI2PCI 0x04
+#define SINGLECMD 0x02
+#define ABORTPENDING 0x01
+
+#define SCSBIST0 0x39
+#define GSBISTERR 0x40
+#define GSBISTDONE 0x20
+#define GSBISTRUN 0x10
+#define OSBISTERR 0x04
+#define OSBISTDONE 0x02
+#define OSBISTRUN 0x01
+
+#define LQCTL2 0x39
+#define LQIRETRY 0x80
+#define LQICONTINUE 0x40
+#define LQITOIDLE 0x20
+#define LQIPAUSE 0x10
+#define LQORETRY 0x08
+#define LQOCONTINUE 0x04
+#define LQOTOIDLE 0x02
+#define LQOPAUSE 0x01
+
+#define SCSBIST1 0x3a
+#define NTBISTERR 0x04
+#define NTBISTDONE 0x02
+#define NTBISTRUN 0x01
+
+#define SCSISEQ0 0x3a
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENARBO 0x20
+#define FORCEBUSFREE 0x10
+#define SCSIRSTO 0x01
+
+#define SCSISEQ1 0x3b
+
+#define SXFRCTL0 0x3c
+#define DFON 0x80
+#define DFPEXP 0x40
+#define BIOSCANCELEN 0x10
+#define SPIOEN 0x08
+
+#define BUSINITID 0x3c
+
+#define DLCOUNT 0x3c
+
+#define SXFRCTL1 0x3d
+#define BITBUCKET 0x80
+#define ENSACHK 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18
+#define ENSTIMER 0x04
+#define ACTNEGEN 0x02
+#define STPWEN 0x01
+
+#define BUSTARGID 0x3e
+
+#define SXFRCTL2 0x3e
+#define AUTORSTDIS 0x10
+#define CMDDMAEN 0x08
+#define ASU 0x07
+
+#define DFFSTAT 0x3f
+#define FIFO1FREE 0x20
+#define FIFO0FREE 0x10
+#define CURRFIFO 0x01
+
+#define SCSISIGO 0x40
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+#define MULTARGID 0x40
+
+#define SCSISIGI 0x41
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+#define SCSIPHASE 0x42
+#define STATUS_PHASE 0x20
+#define COMMAND_PHASE 0x10
+#define MSG_IN_PHASE 0x08
+#define MSG_OUT_PHASE 0x04
+#define DATA_PHASE_MASK 0x03
+#define DATA_IN_PHASE 0x02
+#define DATA_OUT_PHASE 0x01
+
+#define SCSIDAT0_IMG 0x43
+
+#define SCSIDAT 0x44
+
+#define SCSIBUS 0x46
+
+#define TARGIDIN 0x48
+#define CLKOUT 0x80
+#define TARGID 0x0f
+
+#define SELID 0x49
+#define SELID_MASK 0xf0
+#define ONEBIT 0x08
+
+#define SBLKCTL 0x4a
+#define DIAGLEDEN 0x80
+#define DIAGLEDON 0x40
+#define ENAB40 0x08
+#define ENAB20 0x04
+#define SELWIDE 0x02
+
+#define OPTIONMODE 0x4a
+#define BIOSCANCTL 0x80
+#define AUTOACKEN 0x40
+#define BIASCANCTL 0x20
+#define BUSFREEREV 0x10
+#define ENDGFORMCHK 0x04
+#define OPTIONMODE_DEFAULTS 0x02
+#define AUTO_MSGOUT_DE 0x02
+
+#define SSTAT0 0x4b
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define IOERR 0x08
+#define OVERRUN 0x04
+#define SPIORDY 0x02
+#define ARBDO 0x01
+
+#define CLRSINT0 0x4b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRIOERR 0x08
+#define CLROVERRUN 0x04
+#define CLRSPIORDY 0x02
+#define CLRARBDO 0x01
+
+#define SIMODE0 0x4b
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENIOERR 0x08
+#define ENOVERRUN 0x04
+#define ENSPIORDY 0x02
+#define ENARBDO 0x01
+
+#define CLRSINT1 0x4c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRSTRB2FAST 0x02
+#define CLRREQINIT 0x01
+
+#define SSTAT1 0x4c
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define STRB2FAST 0x02
+#define REQINIT 0x01
+
+#define SSTAT2 0x4d
+#define BUSFREE_DFF1 0xc0
+#define BUSFREETIME 0xc0
+#define BUSFREE_DFF0 0x80
+#define BUSFREE_LQO 0x40
+#define NONPACKREQ 0x20
+#define EXP_ACTIVE 0x10
+#define BSYX 0x08
+#define WIDE_RES 0x04
+#define SDONE 0x02
+#define DMADONE 0x01
+
+#define CLRSINT2 0x4d
+#define CLRNONPACKREQ 0x20
+#define CLRWIDE_RES 0x04
+#define CLRSDONE 0x02
+#define CLRDMADONE 0x01
+
+#define SIMODE2 0x4d
+#define ENWIDE_RES 0x04
+#define ENSDONE 0x02
+#define ENDMADONE 0x01
+
+#define PERRDIAG 0x4e
+#define HIZERO 0x80
+#define HIPERR 0x40
+#define PREVPHASE 0x20
+#define PARITYERR 0x10
+#define AIPERR 0x08
+#define CRCERR 0x04
+#define DGFORMERR 0x02
+#define DTERR 0x01
+
+#define LQISTATE 0x4e
+
+#define SOFFCNT 0x4f
+
+#define LQOSTATE 0x4f
+
+#define LQISTAT0 0x50
+#define LQIATNQAS 0x20
+#define LQICRCT1 0x10
+#define LQICRCT2 0x08
+#define LQIBADLQT 0x04
+#define LQIATNLQ 0x02
+#define LQIATNCMD 0x01
+
+#define CLRLQIINTO 0x50
+#define CLRLQIATNQAS 0x20
+#define CLRLQICRCT1 0x10
+#define CLRLQICRCT2 0x08
+#define CLRLQIBADLQT 0x04
+#define CLRLQIATNLQ 0x02
+#define CLRLQIATNCMD 0x01
+
+#define LQIMODE0 0x50
+#define ENLQIATNQASK 0x20
+#define ENLQICRCT1 0x10
+#define ENLQICRCT2 0x08
+#define ENLQIBADLQT 0x04
+#define ENLQIATNLQ 0x02
+#define ENLQIATNCMD 0x01
+
+#define LQIMODE1 0x51
+#define ENLQIPHASE_LQ 0x80
+#define ENLQIPHASE_NLQ 0x40
+#define ENLIQABORT 0x20
+#define ENLQICRCI_LQ 0x10
+#define ENLQICRCI_NLQ 0x08
+#define ENLQIBADLQI 0x04
+#define ENLQIOVERI_LQ 0x02
+#define ENLQIOVERI_NLQ 0x01
+
+#define LQISTAT1 0x51
+#define LQIPHASE_LQ 0x80
+#define LQIPHASE_NLQ 0x40
+#define LQIABORT 0x20
+#define LQICRCI_LQ 0x10
+#define LQICRCI_NLQ 0x08
+#define LQIBADLQI 0x04
+#define LQIOVERI_LQ 0x02
+#define LQIOVERI_NLQ 0x01
+
+#define CLRLQIINT1 0x51
+#define CLRLQIPHASE_LQ 0x80
+#define CLRLQIPHASE_NLQ 0x40
+#define CLRLIQABORT 0x20
+#define CLRLQICRCI_LQ 0x10
+#define CLRLQICRCI_NLQ 0x08
+#define CLRLQIBADLQI 0x04
+#define CLRLQIOVERI_LQ 0x02
+#define CLRLQIOVERI_NLQ 0x01
+
+#define LQISTAT2 0x52
+#define PACKETIZED 0x80
+#define LQIPHASE_OUTPKT 0x40
+#define LQIWORKONLQ 0x20
+#define LQIWAITFIFO 0x10
+#define LQISTOPPKT 0x08
+#define LQISTOPLQ 0x04
+#define LQISTOPCMD 0x02
+#define LQIGSAVAIL 0x01
+
+#define SSTAT3 0x53
+#define NTRAMPERR 0x02
+#define OSRAMPERR 0x01
+
+#define SIMODE3 0x53
+#define ENNTRAMPERR 0x02
+#define ENOSRAMPERR 0x01
+
+#define CLRSINT3 0x53
+#define CLRNTRAMPERR 0x02
+#define CLROSRAMPERR 0x01
+
+#define LQOMODE0 0x54
+#define ENLQOTARGSCBPERR 0x10
+#define ENLQOSTOPT2 0x08
+#define ENLQOATNLQ 0x04
+#define ENLQOATNPKT 0x02
+#define ENLQOTCRC 0x01
+
+#define LQOSTAT0 0x54
+#define LQOTARGSCBPERR 0x10
+#define LQOSTOPT2 0x08
+#define LQOATNLQ 0x04
+#define LQOATNPKT 0x02
+#define LQOTCRC 0x01
+
+#define CLRLQOINT0 0x54
+#define CLRLQOTARGSCBPERR 0x10
+#define CLRLQOSTOPT2 0x08
+#define CLRLQOATNLQ 0x04
+#define CLRLQOATNPKT 0x02
+#define CLRLQOTCRC 0x01
+
+#define LQOSTAT1 0x55
+#define LQOINITSCBPERR 0x10
+#define LQOSTOPI2 0x08
+#define LQOBADQAS 0x04
+#define LQOBUSFREE 0x02
+#define LQOPHACHGINPKT 0x01
+
+#define CLRLQOINT1 0x55
+#define CLRLQOINITSCBPERR 0x10
+#define CLRLQOSTOPI2 0x08
+#define CLRLQOBADQAS 0x04
+#define CLRLQOBUSFREE 0x02
+#define CLRLQOPHACHGINPKT 0x01
+
+#define LQOMODE1 0x55
+#define ENLQOINITSCBPERR 0x10
+#define ENLQOSTOPI2 0x08
+#define ENLQOBADQAS 0x04
+#define ENLQOBUSFREE 0x02
+#define ENLQOPHACHGINPKT 0x01
+
+#define LQOSTAT2 0x56
+#define LQOPKT 0xe0
+#define LQOWAITFIFO 0x10
+#define LQOPHACHGOUTPKT 0x02
+#define LQOSTOP0 0x01
+
+#define OS_SPACE_CNT 0x56
+
+#define SIMODE1 0x57
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENSTRB2FAST 0x02
+#define ENREQINIT 0x01
+
+#define GSFIFO 0x58
+
+#define DFFSXFRCTL 0x5a
+#define CLRSHCNT 0x04
+#define CLRCHN 0x02
+#define RSTCHN 0x01
+
+#define NEXTSCB 0x5a
+
+#define CLRSEQINTSRC 0x5b
+#define CLRCTXTDONE 0x40
+#define CLRSAVEPTRS 0x20
+#define CLRCFG4DATA 0x10
+#define CLRCFG4ISTAT 0x08
+#define CLRCFG4TSTAT 0x04
+#define CLRCFG4ICMD 0x02
+#define CLRCFG4TCMD 0x01
+
+#define SEQINTSRC 0x5b
+#define CTXTDONE 0x40
+#define SAVEPTRS 0x20
+#define CFG4DATA 0x10
+#define CFG4ISTAT 0x08
+#define CFG4TSTAT 0x04
+#define CFG4ICMD 0x02
+#define CFG4TCMD 0x01
+
+#define CURRSCB 0x5c
+
+#define SEQIMODE 0x5c
+#define ENCTXTDONE 0x40
+#define ENSAVEPTRS 0x20
+#define ENCFG4DATA 0x10
+#define ENCFG4ISTAT 0x08
+#define ENCFG4TSTAT 0x04
+#define ENCFG4ICMD 0x02
+#define ENCFG4TCMD 0x01
+
+#define MDFFSTAT 0x5d
+#define LASTSDONE 0x10
+#define SHVALID 0x08
+#define DLZERO 0x04
+#define DATAINFIFO 0x02
+#define FIFOFREE 0x01
+
+#define CRCCONTROL 0x5d
+#define CRCVALCHKEN 0x40
+
+#define DFFTAG 0x5e
+
+#define LASTSCB 0x5e
+
+#define SCSITEST 0x5e
+#define CNTRTEST 0x08
+#define SEL_TXPLL_DEBUG 0x04
+
+#define IOPDNCTL 0x5f
+#define DISABLE_OE 0x80
+#define PDN_IDIST 0x04
+#define PDN_DIFFSENSE 0x01
+
+#define SHADDR 0x60
+
+#define NEGOADDR 0x60
+
+#define DGRPCRCI 0x60
+
+#define NEGPERIOD 0x61
+
+#define PACKCRCI 0x62
+
+#define NEGOFFSET 0x62
+
+#define NEGPPROPTS 0x63
+#define PPROPT_PACE 0x08
+#define PPROPT_QAS 0x04
+#define PPROPT_DT 0x02
+#define PPROPT_IUT 0x01
+
+#define NEGCONOPTS 0x64
+#define ENAIP 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNO 0x02
+#define WIDEXFER 0x01
+
+#define ANNEXCOL 0x65
+
+#define ANNEXDAT 0x66
+
+#define IOWNID 0x67
+
+#define PLL960CTL0 0x68
+
+#define SHCNT 0x68
+
+#define TOWNID 0x69
+
+#define PLL960CTL1 0x69
+
+#define PLL960CNT0 0x6a
+
+#define XSIG 0x6a
+
+#define SELOID 0x6b
+
+#define PLL400CTL0 0x6c
+#define PLL_VCOSEL 0x80
+#define PLL_PWDN 0x40
+#define PLL_NS 0x30
+#define PLL_ENLUD 0x08
+#define PLL_ENLPF 0x04
+#define PLL_DLPF 0x02
+#define PLL_ENFBM 0x01
+
+#define FAIRNESS 0x6c
+
+#define PLL400CTL1 0x6d
+#define PLL_CNTEN 0x80
+#define PLL_CNTCLR 0x40
+#define PLL_RST 0x01
+
+#define PLL400CNT0 0x6e
+
+#define UNFAIRNESS 0x6e
+
+#define HADDR 0x70
+
+#define HODMAADR 0x70
+
+#define HODMACNT 0x78
+
+#define HCNT 0x78
+
+#define HODMAEN 0x7a
+
+#define SGHADDR 0x7c
+
+#define SCBHADDR 0x7c
+
+#define SGHCNT 0x84
+
+#define SCBHCNT 0x84
+
+#define DFF_THRSH 0x88
+#define WR_DFTHRSH 0x70
+#define WR_DFTHRSH_MAX 0x70
+#define WR_DFTHRSH_90 0x60
+#define WR_DFTHRSH_85 0x50
+#define WR_DFTHRSH_75 0x40
+#define WR_DFTHRSH_63 0x30
+#define WR_DFTHRSH_50 0x20
+#define WR_DFTHRSH_25 0x10
+#define RD_DFTHRSH 0x07
+#define RD_DFTHRSH_MAX 0x07
+#define RD_DFTHRSH_90 0x06
+#define RD_DFTHRSH_85 0x05
+#define RD_DFTHRSH_75 0x04
+#define RD_DFTHRSH_63 0x03
+#define RD_DFTHRSH_50 0x02
+#define RD_DFTHRSH_25 0x01
+#define WR_DFTHRSH_MIN 0x00
+#define RD_DFTHRSH_MIN 0x00
+
+#define ROMADDR 0x8a
+
+#define ROMCNTRL 0x8d
+#define ROMOP 0xe0
+#define ROMSPD 0x18
+#define REPEAT 0x02
+#define RDY 0x01
+
+#define ROMDATA 0x8e
+
+#define CMCRXMSG0 0x90
+
+#define ROENABLE 0x90
+#define MSIROEN 0x20
+#define OVLYROEN 0x10
+#define CMCROEN 0x08
+#define SGROEN 0x04
+#define DCH1ROEN 0x02
+#define DCH0ROEN 0x01
+
+#define OVLYRXMSG0 0x90
+
+#define DCHRXMSG0 0x90
+
+#define OVLYRXMSG1 0x91
+
+#define NSENABLE 0x91
+#define MSINSEN 0x20
+#define OVLYNSEN 0x10
+#define CMCNSEN 0x08
+#define SGNSEN 0x04
+#define DCH1NSEN 0x02
+#define DCH0NSEN 0x01
+
+#define DCHRXMSG1 0x91
+
+#define CMCRXMSG1 0x91
+
+#define DCHRXMSG2 0x92
+
+#define OVLYRXMSG2 0x92
+
+#define CMCRXMSG2 0x92
+
+#define OST 0x92
+
+#define DCHRXMSG3 0x93
+
+#define CMCRXMSG3 0x93
+
+#define PCIXCTL 0x93
+#define SERRPULSE 0x80
+#define UNEXPSCIEN 0x20
+#define SPLTSMADIS 0x10
+#define SPLTSTADIS 0x08
+#define SRSPDPEEN 0x04
+#define TSCSERREN 0x02
+#define CMPABCDIS 0x01
+
+#define OVLYRXMSG3 0x93
+
+#define OVLYSEQBCNT 0x94
+
+#define CMCSEQBCNT 0x94
+
+#define DCHSEQBCNT 0x94
+
+#define CMCSPLTSTAT0 0x96
+
+#define OVLYSPLTSTAT0 0x96
+
+#define DCHSPLTSTAT0 0x96
+
+#define DCHSPLTSTAT1 0x97
+
+#define CMCSPLTSTAT1 0x97
+
+#define OVLYSPLTSTAT1 0x97
+
+#define SGRXMSG0 0x98
+#define CDNUM 0xf8
+#define CFNUM 0x07
+
+#define SLVSPLTOUTADR0 0x98
+#define LOWER_ADDR 0x7f
+
+#define SGRXMSG1 0x99
+#define CBNUM 0xff
+
+#define SLVSPLTOUTADR1 0x99
+#define REQ_DNUM 0xf8
+#define REQ_FNUM 0x07
+
+#define SGRXMSG2 0x9a
+#define MINDEX 0xff
+
+#define SLVSPLTOUTADR2 0x9a
+#define REQ_BNUM 0xff
+
+#define SGRXMSG3 0x9b
+#define MCLASS 0x0f
+
+#define SLVSPLTOUTADR3 0x9b
+#define TAG_NUM 0x1f
+#define RLXORD 0x10
+
+#define SGSEQBCNT 0x9c
+
+#define SLVSPLTOUTATTR0 0x9c
+#define LOWER_BCNT 0xff
+
+#define SLVSPLTOUTATTR1 0x9d
+#define CMPLT_DNUM 0xf8
+#define CMPLT_FNUM 0x07
+
+#define SLVSPLTOUTATTR2 0x9e
+#define CMPLT_BNUM 0xff
+
+#define SGSPLTSTAT0 0x9e
+#define STAETERM 0x80
+#define SCBCERR 0x40
+#define SCADERR 0x20
+#define SCDATBUCKET 0x10
+#define CNTNOTCMPLT 0x08
+#define RXOVRUN 0x04
+#define RXSCEMSG 0x02
+#define RXSPLTRSP 0x01
+
+#define SFUNCT 0x9f
+#define TEST_GROUP 0xf0
+#define TEST_NUM 0x0f
+
+#define SGSPLTSTAT1 0x9f
+#define RXDATABUCKET 0x01
+
+#define DF0PCISTAT 0xa0
+
+#define REG0 0xa0
+
+#define DF1PCISTAT 0xa1
+
+#define SGPCISTAT 0xa2
+
+#define REG1 0xa2
+
+#define CMCPCISTAT 0xa3
+
+#define OVLYPCISTAT 0xa4
+#define SCAAPERR 0x08
+#define RDPERR 0x04
+
+#define REG2 0xa4
+
+#define SG_STATE 0xa6
+#define FETCH_INPROG 0x04
+#define LOADING_NEEDED 0x02
+#define SEGS_AVAIL 0x01
+
+#define MSIPCISTAT 0xa6
+#define RMA 0x20
+#define RTA 0x10
+#define CLRPENDMSI 0x08
+#define DPR 0x01
+
+#define TARGPCISTAT 0xa6
+#define DPE 0x80
+#define SSE 0x40
+#define STA 0x08
+#define TWATERR 0x02
+
+#define DATA_COUNT_ODD 0xa7
+
+#define SCBPTR 0xa8
+
+#define CCSCBACNT 0xab
+
+#define SCBAUTOPTR 0xab
+#define AUSCBPTR_EN 0x80
+#define SCBPTR_ADDR 0x38
+#define SCBPTR_OFF 0x07
+
+#define CCSGADDR 0xac
+
+#define CCSCBADDR 0xac
+
+#define CCSCBADR_BK 0xac
+
+#define CMC_RAMBIST 0xad
+#define SG_ELEMENT_SIZE 0x80
+#define SCBRAMBIST_FAIL 0x40
+#define SG_BIST_FAIL 0x20
+#define SG_BIST_EN 0x10
+#define CMC_BUFFER_BIST_FAIL 0x02
+#define CMC_BUFFER_BIST_EN 0x01
+
+#define CCSGCTL 0xad
+#define CCSGDONE 0x80
+#define SG_CACHE_AVAIL 0x10
+#define CCSGEN 0x08
+#define SG_FETCH_REQ 0x02
+#define CCSGRESET 0x01
+
+#define CCSCBCTL 0xad
+#define CCSCBDONE 0x80
+#define ARRDONE 0x40
+#define CCARREN 0x10
+#define CCSCBEN 0x08
+#define CCSCBDIR 0x04
+#define CCSCBRESET 0x01
+
+#define CCSGRAM 0xb0
+
+#define FLEXADR 0xb0
+
+#define CCSCBRAM 0xb0
+
+#define FLEXCNT 0xb3
+
+#define FLEXDMASTAT 0xb5
+#define FLEXDMAERR 0x02
+#define FLEXDMADONE 0x01
+
+#define FLEXDATA 0xb6
+
+#define BRDDAT 0xb8
+
+#define BRDCTL 0xb9
+#define FLXARBACK 0x80
+#define FLXARBREQ 0x40
+#define BRDADDR 0x38
+#define BRDEN 0x04
+#define BRDRW 0x02
+#define BRDSTB 0x01
+
+#define SEEADR 0xba
+
+#define SEEDAT 0xbc
+
+#define SEECTL 0xbe
+#define SEEOP_ERASE 0x70
+#define SEEOPCODE 0x70
+#define SEEOP_READ 0x60
+#define SEEOP_WRITE 0x50
+#define SEEOP_EWEN 0x40
+#define SEEOP_WALL 0x40
+#define SEEOP_ERAL 0x40
+#define SEEOP_EWDS 0x40
+#define SEERST 0x02
+#define SEESTART 0x01
+
+#define SEESTAT 0xbe
+#define INIT_DONE 0x80
+#define LDALTID_L 0x08
+#define SEEARBACK 0x04
+#define SEEBUSY 0x02
+
+#define SCBCNT 0xbf
+
+#define DFWADDR 0xc0
+
+#define DSPFLTRCTL 0xc0
+#define FLTRDISABLE 0x20
+#define EDGESENSE 0x10
+#define DSPFCNTSEL 0x0f
+
+#define CLRSEQINTSTAT 0xc0
+#define CLRSEQ_SWTMRTO 0x10
+#define CLRSEQ_SEQINT 0x08
+#define CLRSEQ_SCSIINT 0x04
+#define CLRSEQ_PCIINT 0x02
+#define CLRSEQ_SPLTINT 0x01
+
+#define DSPDATACTL 0xc1
+#define BYPASSENAB 0x80
+#define DESQDIS 0x10
+#define RCVROFFSTDIS 0x04
+#define XMITOFFSTDIS 0x02
+
+#define DFRADDR 0xc2
+
+#define DSPREQCTL 0xc2
+#define MANREQCTL 0xc0
+#define MANREQDLY 0x3f
+
+#define DSPACKCTL 0xc3
+#define MANACKCTL 0xc0
+#define MANACKDLY 0x3f
+
+#define DFDAT 0xc4
+
+#define DSPSELECT 0xc4
+#define AUTOINCEN 0x80
+#define DSPSEL 0x1f
+
+#define WRTBIASCTL 0xc5
+#define AUTOXBCDIS 0x80
+#define XMITMANVAL 0x3f
+
+#define RCVRBIOSCTL 0xc6
+#define AUTORBCDIS 0x80
+#define RCVRMANVAL 0x3f
+
+#define WRTBIASCALC 0xc7
+
+#define DFPTRS 0xc8
+
+#define RCVRBIASCALC 0xc8
+
+#define DFDBCTL 0xc8
+#define DFF_CIO_WR_RDY 0x20
+#define DFF_CIO_RD_RDY 0x10
+#define DFF_DIR_ERR 0x08
+#define DFF_RAMBIST_FAIL 0x04
+#define DFF_RAMBIST_DONE 0x02
+#define DFF_RAMBIST_EN 0x01
+
+#define DFBKPTR 0xc9
+
+#define SKEWCALC 0xc9
+
+#define DFSCNT 0xcc
+
+#define DFBCNT 0xce
+
+#define OVLYADDR 0xd4
+
+#define SEQCTL0 0xd6
+#define PERRORDIS 0x80
+#define PAUSEDIS 0x40
+#define FAILDIS 0x20
+#define FASTMODE 0x10
+#define BRKADRINTEN 0x08
+#define STEP 0x04
+#define SEQRESET 0x02
+#define LOADRAM 0x01
+
+#define SEQCTL1 0xd7
+#define OVRLAY_DATA_CHK 0x08
+#define RAMBIST_DONE 0x04
+#define RAMBIST_FAIL 0x02
+#define RAMBIST_EN 0x01
+
+#define FLAGS 0xd8
+#define ZERO 0x02
+#define CARRY 0x01
+
+#define SEQINTCTL 0xd9
+#define INTVEC1DSL 0x80
+#define INT1_CONTEXT 0x20
+#define SCS_SEQ_INT1M1 0x10
+#define SCS_SEQ_INT1M0 0x08
+#define INTMASK 0x06
+#define IRET 0x01
+
+#define SEQRAM 0xda
+
+#define PRGMCNT 0xde
+
+#define SWTIMER 0xe0
+
+#define ACCUM 0xe0
+
+#define SINDEX 0xe2
+
+#define DINDEX 0xe4
+
+#define BRKADDR1 0xe6
+#define BRKDIS 0x80
+
+#define BRKADDR0 0xe6
+
+#define ALLONES 0xe8
+
+#define ALLZEROS 0xea
+
+#define NONE 0xea
+
+#define SINDIR 0xec
+
+#define DINDIR 0xed
+
+#define FUNCTION1 0xf0
+
+#define STACK 0xf2
+
+#define CURADDR 0xf4
+
+#define INTVEC1_ADDR 0xf4
+
+#define INTVEC2_ADDR 0xf6
+
+#define LASTADDR 0xf6
+
+#define LONGJMP_ADDR 0xf8
+
+#define LONGJMP_SCB 0xfa
+
+#define ACCUM_SAVE 0xfc
+
+#define WAITING_SCB_TAILS 0x100
+
+#define AHD_PCI_CONFIG_BASE 0x100
+
+#define SRAM_BASE 0x100
+
+#define WAITING_TID_HEAD 0x120
+
+#define WAITING_TID_TAIL 0x122
+
+#define NEXT_QUEUED_SCB_ADDR 0x124
+
+#define COMPLETE_SCB_HEAD 0x128
+
+#define COMPLETE_SCB_DMAINPROG_HEAD 0x12a
+
+#define COMPLETE_DMA_SCB_HEAD 0x12c
+
+#define QFREEZE_COUNT 0x12e
+
+#define SAVED_MODE 0x130
+
+#define MSG_OUT 0x131
+
+#define DMAPARAMS 0x132
+#define PRELOADEN 0x80
+#define WIDEODD 0x40
+#define SCSIEN 0x20
+#define SDMAEN 0x10
+#define SDMAENACK 0x10
+#define HDMAENACK 0x08
+#define HDMAEN 0x08
+#define DIRECTION 0x04
+#define FIFOFLUSH 0x02
+#define FIFORESET 0x01
+
+#define SEQ_FLAGS 0x133
+#define NOT_IDENTIFIED 0x80
+#define NO_CDB_SENT 0x40
+#define TARGET_CMD_IS_TAGGED 0x40
+#define DPHASE 0x20
+#define TARG_CMD_PENDING 0x10
+#define CMDPHASE_PENDING 0x08
+#define DPHASE_PENDING 0x04
+#define SPHASE_PENDING 0x02
+#define NO_DISCONNECT 0x01
+
+#define SAVED_SCSIID 0x134
+
+#define SAVED_LUN 0x135
+
+#define LASTPHASE 0x136
+#define P_MESGIN 0xe0
+#define PHASE_MASK 0xe0
+#define P_STATUS 0xc0
+#define P_MESGOUT 0xa0
+#define CDI 0x80
+#define P_COMMAND 0x80
+#define P_DATAIN_DT 0x60
+#define P_DATAIN 0x40
+#define IOI 0x40
+#define MSGI 0x20
+#define P_DATAOUT_DT 0x20
+#define P_BUSFREE 0x01
+#define P_DATAOUT 0x00
+
+#define SHARED_DATA_ADDR 0x137
+
+#define QOUTFIFO_NEXT_ADDR 0x13b
+
+#define KERNEL_TQINPOS 0x13f
+
+#define TQINPOS 0x140
+
+#define ARG_1 0x141
+#define RETURN_1 0x141
+#define SEND_MSG 0x80
+#define SEND_SENSE 0x40
+#define SEND_REJ 0x20
+#define MSGOUT_PHASEMIS 0x10
+#define EXIT_MSG_LOOP 0x08
+#define CONT_MSG_LOOP_WRITE 0x04
+#define CONT_MSG_LOOP_READ 0x03
+#define CONT_MSG_LOOP_TARG 0x02
+
+#define ARG_2 0x142
+#define RETURN_2 0x142
+
+#define LAST_MSG 0x143
+
+#define SCSISEQ_TEMPLATE 0x144
+#define MANUALCTL 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define MANUALP 0x0c
+#define ENAUTOATNP 0x02
+#define ALTSTIM 0x01
+
+#define INITIATOR_TAG 0x145
+
+#define SEQ_FLAGS2 0x146
+#define SELECTOUT_QFROZEN 0x04
+#define TARGET_MSG_PENDING 0x02
+#define SCB_DMA 0x01
+
+#define CMDSIZE_TABLE 0x147
+
+#define SCB_BASE 0x180
+
+#define SCB_RESIDUAL_DATACNT 0x180
+#define SCB_CDB_STORE 0x180
+
+#define SCB_RESIDUAL_SGPTR 0x184
+#define SCB_CDB_PTR 0x184
+#define SG_ADDR_MASK 0xf8
+#define SG_OVERRUN_RESID 0x02
+
+#define SCB_SCSI_STATUS 0x188
+
+#define SCB_TARGET_PHASES 0x189
+
+#define SCB_TARGET_DATA_DIR 0x18a
+
+#define SCB_TARGET_ITAG 0x18b
+
+#define SCB_SENSE_BUSADDR 0x18c
+#define SCB_NEXT_COMPLETE 0x18c
+
+#define SCB_CDB_LEN 0x190
+#define SCB_CDB_LEN_PTR 0x80
+
+#define SCB_TASK_MANAGEMENT 0x191
+
+#define SCB_TAG 0x192
+
+#define SCB_NEXT 0x194
+#define SCB_NEXT_SCB_BUSADDR 0x194
+
+#define SCB_NEXT2 0x196
+
+#define SCB_DATAPTR 0x198
+
+#define SCB_DATACNT 0x1a0
+#define SG_LAST_SEG 0x80
+#define SG_HIGH_ADDR_BITS 0x7f
+
+#define SCB_SGPTR 0x1a4
+#define SG_STATUS_VALID 0x04
+#define SG_FULL_RESID 0x02
+#define SG_LIST_NULL 0x01
+
+#define SCB_CONTROL 0x1a8
+#define TARGET_SCB 0x80
+#define DISCENB 0x40
+#define TAG_ENB 0x20
+#define MK_MESSAGE 0x10
+#define STATUS_RCVD 0x08
+#define DISCONNECTED 0x04
+#define SCB_TAG_TYPE 0x03
+
+#define SCB_SCSIID 0x1a9
+#define TID 0xf0
+#define OID 0x0f
+
+#define SCB_LUN 0x1aa
+#define LID 0xff
+
+#define SCB_TASK_ATTRIBUTE 0x1ab
+#define SCB_NONPACKET_TAG 0x1ab
+
+#define SCB_BUSADDR 0x1ac
+
+#define SCB_DISCONNECTED_LISTS 0x1b0
+
+
+#define SCB_TRANSFER_SIZE 0x30
+#define STATUS_QUEUE_FULL 0x28
+#define STATUS_BUSY 0x08
+#define MAX_OFFSET_PACED 0x7f
+#define BUS_32_BIT 0x02
+#define CCSGADDR_MAX 0x80
+#define TID_SHIFT 0x04
+#define MK_MESSAGE_BIT_OFFSET 0x04
+#define SEEOP_EWDS_ADDR 0x00
+#define AHD_NUM_ANNEXCOLS 0x04
+#define AHD_PRECOMP_FASTSLEW 0x40
+#define AHD_PRECOMP_CUTBACK_29 0x06
+#define NVRAM_SCB_OFFSET 0x2c
+#define STATUS_PKT_SENSE 0xff
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define AHD_SENSE_BUFSIZE 0x100
+#define BUS_8_BIT 0x00
+#define STIMESEL_BUG_ADJ 0x08
+#define STIMESEL_MIN 0x18
+#define STIMESEL_SHIFT 0x03
+#define CCSGRAM_MAXSEGS 0x10
+#define INVALID_ADDR 0x80
+#define TARGET_CMD_CMPLT 0xfe
+#define SEEOP_WRAL_ADDR 0x40
+#define SEEOP_ERAL_ADDR 0x80
+#define AHD_PRECOMP_CUTBACK_37 0x07
+#define AHD_PRECOMP_CUTBACK_17 0x04
+#define AHD_PRECOMP_MASK 0x07
+#define AHD_ANNEXCOL_PRECOMP 0x04
+#define SRC_MODE_SHIFT 0x00
+#define PKT_OVERRUN_BUFSIZE 0x200
+#define TARGET_DATA_IN 0x01
+#define HOST_MSG 0xff
+#define MAX_OFFSET 0xfe
+#define BUS_16_BIT 0x01
+#define CCSCBADDR_MAX 0x80
+#define SEQ_STACK_SIZE 0x08
+#define WRTBIASCTL_CPQ_DEFAULT 0x97
+#define NUMDSPS 0x14
+#define SEEOP_EWEN_ADDR 0xc0
+#define DST_MODE_SHIFT 0x04
+
+
+/* Downloaded Constant Definitions */
+#define PKT_OVERRUN_BUFOFFSET 0x05
+#define SG_SIZEOF 0x04
+#define SG_PREFETCH_ADDR_MASK 0x03
+#define SG_PREFETCH_ALIGN_MASK 0x02
+#define SG_PREFETCH_CNT_LIMIT 0x01
+#define SG_PREFETCH_CNT 0x00
+#define DOWNLOAD_CONST_COUNT 0x06
+
+
+/* Exported Labels */
+#define LABEL_seq_isr 0x215
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_seq.h b/xen/drivers/scsi/aic7xxx/aic79xx_seq.h
new file mode 100644
index 0000000000..eafb47e585
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic79xx_seq.h
@@ -0,0 +1,824 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#42 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#34 $
+ */
+static uint8_t seqprog[] = {
+ 0x33, 0xea, 0xda, 0x58,
+ 0x60, 0x3a, 0x0c, 0x68,
+ 0x04, 0x46, 0x0d, 0x68,
+ 0xff, 0x21, 0x0d, 0x70,
+ 0x40, 0x4b, 0x16, 0x69,
+ 0x00, 0xe2, 0xe0, 0x58,
+ 0x40, 0x4b, 0x16, 0x69,
+ 0x20, 0x4b, 0x0a, 0x69,
+ 0xfc, 0x42, 0x16, 0x78,
+ 0x10, 0x40, 0x16, 0x78,
+ 0x00, 0xe2, 0xf4, 0x5c,
+ 0x20, 0x4d, 0x1a, 0x78,
+ 0x00, 0xe2, 0xf4, 0x5c,
+ 0x01, 0x52, 0x3c, 0x78,
+ 0x02, 0x58, 0x50, 0x31,
+ 0xff, 0xea, 0x10, 0x0b,
+ 0x08, 0xa8, 0x51, 0x03,
+ 0x01, 0xa4, 0x29, 0x78,
+ 0x00, 0xe2, 0xc6, 0x5a,
+ 0x00, 0xe2, 0x1a, 0x40,
+ 0xff, 0xea, 0xd4, 0x19,
+ 0x02, 0xa8, 0x82, 0x32,
+ 0x00, 0xea, 0xda, 0x58,
+ 0x00, 0xe2, 0xe8, 0x5c,
+ 0x00, 0xe2, 0x3c, 0x48,
+ 0x11, 0xea, 0xda, 0x58,
+ 0x00, 0xe2, 0xe8, 0x5c,
+ 0x00, 0xe2, 0x3c, 0x48,
+ 0x33, 0xea, 0xda, 0x58,
+ 0x00, 0xe2, 0xbe, 0x5a,
+ 0x00, 0xea, 0xda, 0x58,
+ 0x80, 0xf9, 0x42, 0x68,
+ 0x00, 0xe2, 0xd8, 0x58,
+ 0x11, 0xea, 0xda, 0x58,
+ 0x80, 0xf9, 0x48, 0x68,
+ 0x00, 0xe2, 0xd8, 0x58,
+ 0x00, 0xe2, 0x4c, 0x58,
+ 0x00, 0xe2, 0x00, 0x40,
+ 0x22, 0xea, 0xda, 0x58,
+ 0x18, 0xad, 0x94, 0x78,
+ 0x04, 0xad, 0x64, 0x68,
+ 0x80, 0xad, 0xa0, 0x7c,
+ 0x10, 0xad, 0x58, 0x78,
+ 0xe7, 0xad, 0x5a, 0x0d,
+ 0xe7, 0xad, 0x5a, 0x09,
+ 0x02, 0xea, 0x02, 0x00,
+ 0xff, 0xea, 0x56, 0x02,
+ 0x04, 0x7c, 0x76, 0x32,
+ 0x20, 0x16, 0xa0, 0x7c,
+ 0x04, 0x37, 0x77, 0x36,
+ 0x40, 0xad, 0xa0, 0x7c,
+ 0xe7, 0xad, 0x5a, 0x09,
+ 0x02, 0xa8, 0x40, 0x31,
+ 0x04, 0x94, 0x49, 0x32,
+ 0xff, 0xea, 0x2a, 0x03,
+ 0xff, 0xea, 0x2e, 0x03,
+ 0x10, 0xa8, 0x85, 0x68,
+ 0x3d, 0xa9, 0xc5, 0x29,
+ 0xfe, 0xe2, 0xc4, 0x09,
+ 0x01, 0xea, 0xc6, 0x01,
+ 0x02, 0xe2, 0xc8, 0x31,
+ 0x02, 0xec, 0x50, 0x31,
+ 0x02, 0xa0, 0xda, 0x31,
+ 0xff, 0xa9, 0x84, 0x70,
+ 0x02, 0xa0, 0x28, 0x33,
+ 0x01, 0x10, 0xd4, 0x35,
+ 0xff, 0x21, 0x8f, 0x70,
+ 0x02, 0x22, 0x51, 0x31,
+ 0x02, 0xa0, 0x2c, 0x33,
+ 0x02, 0xa0, 0x44, 0x32,
+ 0x01, 0x10, 0xd4, 0x35,
+ 0x02, 0xa0, 0x40, 0x32,
+ 0x02, 0xa0, 0x44, 0x32,
+ 0x01, 0x10, 0xd4, 0x35,
+ 0x04, 0x46, 0x99, 0x68,
+ 0x40, 0x16, 0xbc, 0x68,
+ 0xff, 0x2d, 0xc1, 0x60,
+ 0xff, 0x29, 0xa1, 0x74,
+ 0x02, 0x28, 0x55, 0x32,
+ 0x01, 0xea, 0x5a, 0x01,
+ 0x04, 0x3b, 0xf9, 0x30,
+ 0x02, 0x28, 0x61, 0x31,
+ 0x02, 0x28, 0x51, 0x31,
+ 0x00, 0xe2, 0xac, 0x40,
+ 0x02, 0x8c, 0x61, 0x31,
+ 0x02, 0x8c, 0x51, 0x31,
+ 0x01, 0x14, 0xd4, 0x31,
+ 0xff, 0x8d, 0xb5, 0x70,
+ 0x80, 0xac, 0xb4, 0x70,
+ 0x20, 0x16, 0xa8, 0x78,
+ 0x01, 0xac, 0x08, 0x31,
+ 0x09, 0xea, 0x5a, 0x01,
+ 0x02, 0x8c, 0x51, 0x32,
+ 0xff, 0xea, 0x1a, 0x07,
+ 0x04, 0x24, 0xf9, 0x30,
+ 0x1d, 0xea, 0xcc, 0x40,
+ 0x02, 0x2c, 0x51, 0x31,
+ 0x04, 0xac, 0xf9, 0x30,
+ 0x19, 0xea, 0xcc, 0x58,
+ 0x02, 0x8c, 0x59, 0x32,
+ 0x02, 0x28, 0x19, 0x33,
+ 0x02, 0xa8, 0x50, 0x36,
+ 0x30, 0xea, 0x08, 0x01,
+ 0x01, 0xe2, 0x5a, 0x31,
+ 0x01, 0x46, 0x8d, 0x06,
+ 0x02, 0xa8, 0xf4, 0x31,
+ 0x02, 0xf2, 0xf0, 0x35,
+ 0x02, 0xf2, 0xf0, 0x31,
+ 0x02, 0xf8, 0xe4, 0x35,
+ 0x80, 0xea, 0xb2, 0x01,
+ 0x01, 0xe2, 0x00, 0x30,
+ 0xff, 0xea, 0xb2, 0x0d,
+ 0x02, 0x20, 0xbd, 0x30,
+ 0x02, 0x20, 0xb9, 0x30,
+ 0x02, 0x20, 0x51, 0x31,
+ 0x4c, 0xa9, 0xd7, 0x28,
+ 0x10, 0xa8, 0xef, 0x78,
+ 0x01, 0x6b, 0xc0, 0x30,
+ 0x02, 0x64, 0xc8, 0x00,
+ 0x40, 0x3a, 0x74, 0x04,
+ 0x00, 0xea, 0xda, 0x58,
+ 0x80, 0xf9, 0xf6, 0x68,
+ 0x00, 0xe2, 0xd8, 0x58,
+ 0x11, 0xea, 0xda, 0x58,
+ 0x80, 0xf9, 0xfc, 0x68,
+ 0x00, 0xe2, 0xd8, 0x58,
+ 0x33, 0xea, 0xda, 0x58,
+ 0x30, 0x3f, 0xc0, 0x09,
+ 0x30, 0xe0, 0xf0, 0x60,
+ 0x02, 0xa8, 0x82, 0x32,
+ 0x01, 0x3f, 0x7e, 0x00,
+ 0x11, 0xea, 0xda, 0x58,
+ 0x02, 0x41, 0x51, 0x35,
+ 0x08, 0x3c, 0x78, 0x00,
+ 0xf0, 0x49, 0x68, 0x0a,
+ 0x0f, 0x67, 0xc0, 0x09,
+ 0x00, 0x34, 0x69, 0x02,
+ 0x20, 0xea, 0x96, 0x00,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0x40, 0x3a, 0x32, 0x69,
+ 0x02, 0x55, 0x00, 0x68,
+ 0x02, 0x56, 0x32, 0x69,
+ 0xff, 0x5b, 0x32, 0x61,
+ 0x02, 0x20, 0x51, 0x31,
+ 0x80, 0xea, 0xb2, 0x01,
+ 0x44, 0xea, 0x00, 0x00,
+ 0x01, 0x33, 0xc0, 0x31,
+ 0x33, 0xea, 0x00, 0x00,
+ 0xff, 0xea, 0xb2, 0x09,
+ 0xff, 0xe0, 0xc0, 0x19,
+ 0xff, 0xe0, 0x34, 0x79,
+ 0x02, 0x94, 0x51, 0x31,
+ 0x00, 0xe2, 0x2a, 0x41,
+ 0x02, 0x5e, 0x50, 0x31,
+ 0x02, 0xa8, 0xb8, 0x30,
+ 0x02, 0x5c, 0x50, 0x31,
+ 0xff, 0x95, 0x45, 0x71,
+ 0x02, 0x94, 0x41, 0x31,
+ 0x02, 0x22, 0x51, 0x31,
+ 0x02, 0xa0, 0x2c, 0x33,
+ 0x02, 0xa0, 0x44, 0x32,
+ 0x00, 0xe2, 0x4e, 0x41,
+ 0x10, 0xa8, 0x4f, 0x69,
+ 0x3d, 0xa9, 0xc9, 0x29,
+ 0x01, 0xe4, 0xc8, 0x01,
+ 0x01, 0xea, 0xca, 0x01,
+ 0xff, 0xea, 0xda, 0x01,
+ 0x02, 0x20, 0x51, 0x31,
+ 0x02, 0x96, 0x41, 0x32,
+ 0xff, 0x21, 0x57, 0x61,
+ 0xff, 0xea, 0x46, 0x02,
+ 0x02, 0x5c, 0x50, 0x31,
+ 0x40, 0xea, 0x96, 0x00,
+ 0x02, 0x56, 0xfc, 0x6c,
+ 0x01, 0x55, 0xfc, 0x6c,
+ 0x10, 0xa8, 0x63, 0x79,
+ 0x10, 0x40, 0x64, 0x69,
+ 0x01, 0x56, 0x00, 0x68,
+ 0xbf, 0x3a, 0x74, 0x08,
+ 0x01, 0xa9, 0x69, 0x32,
+ 0x01, 0xaa, 0x6b, 0x32,
+ 0x08, 0x3c, 0x78, 0x00,
+ 0x80, 0xea, 0x62, 0x02,
+ 0x40, 0xea, 0x66, 0x02,
+ 0x00, 0xe2, 0x2a, 0x5b,
+ 0x01, 0x36, 0xc1, 0x31,
+ 0x9f, 0xe0, 0xc6, 0x7b,
+ 0x80, 0xe0, 0x82, 0x71,
+ 0xa0, 0xe0, 0xb4, 0x71,
+ 0xc0, 0xe0, 0xa6, 0x71,
+ 0xe0, 0xe0, 0xe4, 0x71,
+ 0x01, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0xee, 0x00, 0x86, 0x69,
+ 0x05, 0xea, 0xb4, 0x00,
+ 0x00, 0xe2, 0xfe, 0x58,
+ 0xef, 0x90, 0xd5, 0x19,
+ 0x00, 0xe2, 0x94, 0x51,
+ 0x0b, 0x84, 0xe1, 0x30,
+ 0x02, 0xea, 0x36, 0x00,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0x00, 0xe2, 0x9c, 0x41,
+ 0x01, 0x90, 0xd1, 0x30,
+ 0x02, 0xea, 0xd2, 0x30,
+ 0x10, 0x80, 0x89, 0x31,
+ 0x20, 0xea, 0x32, 0x00,
+ 0x20, 0x19, 0x9c, 0x69,
+ 0xbf, 0x33, 0x67, 0x0a,
+ 0x02, 0x4d, 0x70, 0x69,
+ 0x40, 0x33, 0x67, 0x02,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0x80, 0x33, 0xad, 0x79,
+ 0x03, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x16, 0x42,
+ 0x01, 0x44, 0x10, 0x33,
+ 0x08, 0xa8, 0x51, 0x03,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0x10, 0xea, 0x80, 0x00,
+ 0x01, 0x31, 0xc5, 0x31,
+ 0x80, 0xe2, 0xd0, 0x61,
+ 0x10, 0xa8, 0xf5, 0x69,
+ 0xc0, 0xaa, 0xc5, 0x01,
+ 0x40, 0xa8, 0xc1, 0x69,
+ 0xbf, 0xe2, 0xc4, 0x09,
+ 0x20, 0xa8, 0xd5, 0x79,
+ 0x01, 0xe2, 0x88, 0x30,
+ 0x00, 0xe2, 0x2a, 0x5b,
+ 0xa0, 0x36, 0xdd, 0x61,
+ 0x23, 0xa8, 0x89, 0x08,
+ 0x00, 0xe2, 0x2a, 0x5b,
+ 0xa0, 0x36, 0xdd, 0x61,
+ 0x00, 0xab, 0xd5, 0x41,
+ 0xff, 0xe2, 0xd4, 0x61,
+ 0x00, 0xe2, 0xf4, 0x41,
+ 0x40, 0xea, 0x98, 0x00,
+ 0x01, 0xe2, 0x88, 0x30,
+ 0x00, 0xe2, 0x2a, 0x5b,
+ 0xa0, 0x36, 0xb3, 0x71,
+ 0x40, 0xea, 0x98, 0x00,
+ 0x01, 0x31, 0x87, 0x32,
+ 0x08, 0xea, 0x62, 0x02,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0xe0, 0xea, 0x36, 0x5b,
+ 0x80, 0xe0, 0x1a, 0x6a,
+ 0x04, 0xe0, 0xd4, 0x72,
+ 0x02, 0xe0, 0x0c, 0x73,
+ 0x00, 0xea, 0x9a, 0x72,
+ 0x03, 0xe0, 0x1c, 0x73,
+ 0x23, 0xe0, 0x04, 0x72,
+ 0x08, 0xe0, 0x16, 0x72,
+ 0x00, 0xe2, 0x2a, 0x5b,
+ 0x07, 0xea, 0x04, 0x00,
+ 0x08, 0x41, 0x71, 0x71,
+ 0x04, 0x41, 0x01, 0x62,
+ 0x01, 0x42, 0x89, 0x30,
+ 0x00, 0xe2, 0xf4, 0x41,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0xf4, 0x41,
+ 0x4c, 0x34, 0xc1, 0x28,
+ 0x01, 0x64, 0x14, 0x7a,
+ 0xa0, 0xea, 0x2e, 0x5b,
+ 0x01, 0xa0, 0x14, 0x62,
+ 0x01, 0x84, 0x11, 0x7a,
+ 0x01, 0xa7, 0x16, 0x7a,
+ 0x05, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x16, 0x42,
+ 0x07, 0xea, 0x3e, 0x5b,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0x4c, 0x34, 0xc5, 0x29,
+ 0x3f, 0xe0, 0x6a, 0x0a,
+ 0x01, 0x35, 0x51, 0x31,
+ 0x3d, 0xe2, 0x52, 0x29,
+ 0x07, 0xe2, 0xc4, 0x09,
+ 0x11, 0xe2, 0xc4, 0x29,
+ 0xb0, 0xe2, 0xc4, 0x19,
+ 0x01, 0xea, 0xc6, 0x01,
+ 0x02, 0xe2, 0xc8, 0x31,
+ 0x02, 0xec, 0x50, 0x31,
+ 0xff, 0xa9, 0x36, 0x72,
+ 0x20, 0xa8, 0x37, 0x6a,
+ 0x04, 0x92, 0x45, 0x31,
+ 0x00, 0xe2, 0x70, 0x42,
+ 0x80, 0x33, 0x67, 0x02,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0x2a, 0x5b,
+ 0x01, 0x33, 0x67, 0x02,
+ 0xe0, 0x36, 0x97, 0x62,
+ 0x02, 0x33, 0x67, 0x02,
+ 0x20, 0x46, 0x92, 0x62,
+ 0x02, 0xa8, 0x40, 0x31,
+ 0xff, 0xea, 0x52, 0x09,
+ 0xa8, 0xea, 0x2e, 0x5b,
+ 0x01, 0xa8, 0x44, 0x31,
+ 0x01, 0xa2, 0xc0, 0x31,
+ 0x00, 0xab, 0x59, 0x62,
+ 0x01, 0x34, 0xc1, 0x31,
+ 0x00, 0xa9, 0x59, 0x62,
+ 0x01, 0x35, 0xc1, 0x31,
+ 0x00, 0xaa, 0x81, 0x72,
+ 0x01, 0xa9, 0x52, 0x11,
+ 0xff, 0xa9, 0x4c, 0x6a,
+ 0x02, 0xa0, 0x50, 0x31,
+ 0xff, 0xea, 0x42, 0x01,
+ 0x01, 0xa2, 0xc0, 0x31,
+ 0x00, 0xab, 0x6d, 0x72,
+ 0x02, 0xa8, 0x40, 0x31,
+ 0x02, 0x94, 0x51, 0x31,
+ 0xff, 0xa9, 0x62, 0x62,
+ 0x00, 0xe2, 0x92, 0x42,
+ 0x04, 0x92, 0x45, 0x31,
+ 0xff, 0xa1, 0x7a, 0x62,
+ 0x4c, 0x34, 0xc5, 0x29,
+ 0x01, 0x35, 0x51, 0x31,
+ 0x3d, 0xe2, 0x52, 0x29,
+ 0x02, 0xa4, 0xda, 0x31,
+ 0x00, 0xe2, 0x7e, 0x42,
+ 0x02, 0xa0, 0x50, 0x31,
+ 0x02, 0xa4, 0x28, 0x33,
+ 0x02, 0xa2, 0x50, 0x31,
+ 0x10, 0x33, 0x67, 0x02,
+ 0x04, 0xa8, 0x93, 0x7a,
+ 0xfb, 0xa8, 0x51, 0x0b,
+ 0xff, 0xea, 0x66, 0x0a,
+ 0x01, 0xa4, 0x8d, 0x6a,
+ 0x00, 0xe2, 0xfe, 0x58,
+ 0x10, 0xa8, 0x17, 0x7a,
+ 0xff, 0xea, 0x3e, 0x5b,
+ 0x00, 0xe2, 0x16, 0x42,
+ 0x04, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x16, 0x42,
+ 0x04, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0xc0, 0x33, 0xa1, 0x7a,
+ 0x03, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x16, 0x42,
+ 0x08, 0xa8, 0x9d, 0x7a,
+ 0x10, 0xa8, 0xa7, 0x7a,
+ 0x0a, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xbe, 0x5a,
+ 0x00, 0xe2, 0xfc, 0x42,
+ 0x40, 0x4b, 0xae, 0x6a,
+ 0xbf, 0x3a, 0x74, 0x08,
+ 0x01, 0xe0, 0xf8, 0x31,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x01, 0x2e, 0x5d, 0x1a,
+ 0x00, 0x2f, 0x5f, 0x22,
+ 0x04, 0x46, 0x8d, 0x02,
+ 0x01, 0xfc, 0xc0, 0x35,
+ 0x33, 0xea, 0xda, 0x58,
+ 0x02, 0x41, 0x51, 0x31,
+ 0xff, 0x88, 0xcb, 0x6a,
+ 0x01, 0xa4, 0xc7, 0x6a,
+ 0x02, 0xa4, 0xcf, 0x6a,
+ 0x01, 0x84, 0xcf, 0x7a,
+ 0x02, 0x28, 0x19, 0x33,
+ 0x02, 0xa8, 0x50, 0x36,
+ 0xff, 0x88, 0xcf, 0x72,
+ 0x00, 0xe2, 0xaa, 0x5a,
+ 0x02, 0x2c, 0x19, 0x33,
+ 0x02, 0xa8, 0x58, 0x32,
+ 0x04, 0xa4, 0x49, 0x07,
+ 0xc0, 0x33, 0xdb, 0x7a,
+ 0x03, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x16, 0x42,
+ 0x04, 0xa8, 0x51, 0x03,
+ 0x20, 0xa8, 0xe3, 0x7a,
+ 0x01, 0xab, 0xc1, 0x31,
+ 0x00, 0xa8, 0xfc, 0x72,
+ 0x02, 0xa8, 0x40, 0x31,
+ 0x4c, 0xa9, 0xc5, 0x29,
+ 0x01, 0xaa, 0x51, 0x31,
+ 0x3d, 0xe2, 0x52, 0x29,
+ 0x07, 0xe2, 0xc4, 0x09,
+ 0x11, 0xe2, 0xc4, 0x29,
+ 0xb0, 0xe2, 0xc4, 0x19,
+ 0x01, 0xea, 0xc6, 0x01,
+ 0x02, 0xe2, 0xc8, 0x31,
+ 0x02, 0xec, 0x44, 0x31,
+ 0x02, 0xa0, 0xda, 0x31,
+ 0x02, 0xa0, 0x50, 0x31,
+ 0x02, 0xa2, 0x28, 0x33,
+ 0xf7, 0x57, 0xae, 0x08,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0x26, 0x5b,
+ 0xee, 0x00, 0x06, 0x6b,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x09, 0x4c, 0x06, 0x7b,
+ 0x08, 0x4c, 0x00, 0x68,
+ 0x0b, 0xea, 0x04, 0x00,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x20, 0x33, 0x71, 0x79,
+ 0x00, 0xe2, 0x14, 0x5b,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0x01, 0x84, 0x19, 0x7b,
+ 0x01, 0xa4, 0x49, 0x07,
+ 0x08, 0x60, 0x30, 0x33,
+ 0x08, 0x80, 0x41, 0x37,
+ 0xdf, 0x33, 0x67, 0x0a,
+ 0xee, 0x00, 0x22, 0x6b,
+ 0x05, 0xea, 0xb4, 0x00,
+ 0x00, 0xe2, 0xfe, 0x58,
+ 0x00, 0xe2, 0x16, 0x42,
+ 0x01, 0xea, 0x6c, 0x02,
+ 0xc0, 0xea, 0x66, 0x06,
+ 0xff, 0x42, 0x2a, 0x7b,
+ 0xe0, 0x41, 0x6c, 0x0e,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0xff, 0x42, 0x30, 0x7b,
+ 0xe0, 0x41, 0x6c, 0x0a,
+ 0xe0, 0x36, 0x71, 0x61,
+ 0xff, 0xea, 0xca, 0x09,
+ 0x01, 0xe2, 0xc8, 0x31,
+ 0x01, 0x46, 0xda, 0x35,
+ 0x01, 0x44, 0xd4, 0x35,
+ 0x10, 0xea, 0x80, 0x00,
+ 0x01, 0xe2, 0x62, 0x36,
+ 0x04, 0xa6, 0xa0, 0x7c,
+ 0xff, 0xea, 0x4c, 0x09,
+ 0xff, 0xea, 0x5a, 0x0d,
+ 0x80, 0xea, 0xb2, 0x01,
+ 0x11, 0x00, 0x00, 0x10,
+ 0xff, 0xea, 0xb2, 0x0d,
+ 0x01, 0x00, 0x60, 0x32,
+ 0x02, 0xa6, 0x54, 0x7b,
+ 0x00, 0xe2, 0x5e, 0x5b,
+ 0x00, 0xe2, 0x48, 0x5b,
+ 0x02, 0xa6, 0x5a, 0x7b,
+ 0x00, 0xe2, 0x5e, 0x5b,
+ 0x00, 0xe2, 0x4c, 0x58,
+ 0x00, 0x30, 0xdb, 0x40,
+ 0x01, 0xa6, 0x78, 0x6b,
+ 0x10, 0xad, 0xa0, 0x7c,
+ 0x98, 0xad, 0x70, 0x73,
+ 0x08, 0xad, 0xa0, 0x6c,
+ 0x00, 0xea, 0x08, 0x81,
+ 0x02, 0x84, 0xf9, 0x88,
+ 0x03, 0x85, 0xfb, 0x30,
+ 0x19, 0xea, 0x5a, 0x01,
+ 0x04, 0xa6, 0x4c, 0x05,
+ 0x04, 0xa6, 0xa0, 0x7c,
+ 0x00, 0xe2, 0x46, 0x5b,
+ 0x03, 0x84, 0x59, 0x89,
+ 0x03, 0xea, 0x4c, 0x01,
+ 0x80, 0x1a, 0xa0, 0x7c,
+ 0x08, 0xb0, 0xe0, 0x30,
+ 0x04, 0xb0, 0xe0, 0x30,
+ 0x03, 0xb0, 0xf0, 0x30,
+ 0x01, 0x78, 0x84, 0x7b,
+ 0x01, 0xa7, 0x4e, 0x11,
+ 0x01, 0xb0, 0x06, 0x33,
+ 0x7f, 0x83, 0xe9, 0x08,
+ 0x04, 0xac, 0x58, 0x19,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x04, 0x84, 0x09, 0x9b,
+ 0x00, 0x85, 0x0b, 0x23,
+ 0x00, 0x86, 0x0d, 0x23,
+ 0x00, 0x87, 0x0f, 0x23,
+ 0x01, 0x84, 0xc5, 0x31,
+ 0x01, 0xa7, 0x9a, 0x7b,
+ 0x04, 0xe2, 0xc4, 0x01,
+ 0x80, 0x83, 0xa1, 0x7b,
+ 0x02, 0xe2, 0xc4, 0x01,
+ 0xff, 0xea, 0x4c, 0x09,
+ 0x01, 0xe2, 0x36, 0x30,
+ 0x88, 0x19, 0x32, 0x00,
+ 0x01, 0xac, 0xd4, 0x99,
+ 0x00, 0xe2, 0xa0, 0x54,
+ 0xfe, 0xa6, 0x4c, 0x0d,
+ 0x0b, 0x98, 0xe1, 0x30,
+ 0x01, 0xa0, 0x4f, 0x09,
+ 0xfd, 0xa4, 0x41, 0x09,
+ 0x80, 0xa3, 0xb5, 0x7b,
+ 0x02, 0xa0, 0x40, 0x01,
+ 0x01, 0xa7, 0xb8, 0x7b,
+ 0x04, 0xa0, 0x40, 0x01,
+ 0x01, 0xa0, 0x36, 0x30,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0xfd, 0xa4, 0x49, 0x0b,
+ 0x05, 0xa3, 0x07, 0x33,
+ 0x80, 0x83, 0xc5, 0x6b,
+ 0x02, 0xea, 0x4c, 0x05,
+ 0xff, 0xea, 0x4c, 0x0d,
+ 0xc0, 0x33, 0xcb, 0x7b,
+ 0x03, 0xea, 0x04, 0x00,
+ 0x20, 0x33, 0xe3, 0x7b,
+ 0x01, 0x84, 0xd1, 0x6b,
+ 0x06, 0xea, 0x04, 0x00,
+ 0xee, 0x00, 0xd6, 0x6b,
+ 0x05, 0xea, 0xb4, 0x00,
+ 0x33, 0xea, 0xda, 0x58,
+ 0x80, 0x3d, 0x7a, 0x00,
+ 0x03, 0x42, 0xd8, 0x6b,
+ 0x7f, 0x3d, 0x7a, 0x08,
+ 0x11, 0xea, 0xda, 0x58,
+ 0x09, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0x01, 0xa4, 0xd1, 0x6b,
+ 0x00, 0xe2, 0xaa, 0x5b,
+ 0x20, 0x33, 0x67, 0x02,
+ 0x00, 0xe2, 0x4e, 0x5b,
+ 0x20, 0x19, 0xe8, 0x6b,
+ 0x00, 0xe2, 0x06, 0x5c,
+ 0x04, 0x19, 0xfa, 0x6b,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x01, 0x84, 0xfb, 0x7b,
+ 0x01, 0x1b, 0xf4, 0x7b,
+ 0x01, 0x1a, 0xfa, 0x6b,
+ 0x00, 0xe2, 0xc6, 0x43,
+ 0x80, 0x4b, 0x00, 0x6c,
+ 0x01, 0x4c, 0xfc, 0x7b,
+ 0x03, 0x42, 0xc6, 0x6b,
+ 0x00, 0xe2, 0x42, 0x5b,
+ 0x04, 0x33, 0x71, 0x79,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0x02, 0x1b, 0x0e, 0x7c,
+ 0x08, 0x5d, 0x0c, 0x7c,
+ 0x03, 0x68, 0x00, 0x37,
+ 0x01, 0x84, 0x09, 0x07,
+ 0x08, 0x5d, 0x14, 0x6c,
+ 0x00, 0xe2, 0x4e, 0x5b,
+ 0x00, 0xe2, 0x06, 0x44,
+ 0x80, 0x1b, 0x1e, 0x7c,
+ 0x80, 0x84, 0x1f, 0x6c,
+ 0xff, 0x85, 0x0b, 0x1b,
+ 0xff, 0x86, 0x0d, 0x23,
+ 0xff, 0x87, 0x0f, 0x23,
+ 0xf8, 0x1b, 0x08, 0x0b,
+ 0xff, 0xea, 0x4e, 0x09,
+ 0x04, 0x1b, 0x26, 0x7c,
+ 0x01, 0xa7, 0x4e, 0x01,
+ 0xff, 0xea, 0x06, 0x0b,
+ 0x03, 0x68, 0x00, 0x37,
+ 0xff, 0xea, 0xd4, 0x09,
+ 0x20, 0x5b, 0x36, 0x6c,
+ 0x10, 0x5b, 0x40, 0x6c,
+ 0x08, 0x5b, 0x48, 0x6c,
+ 0x02, 0x5b, 0x44, 0x6d,
+ 0x12, 0xea, 0x04, 0x00,
+ 0x08, 0x19, 0x3c, 0x7c,
+ 0xdf, 0x5c, 0xb8, 0x08,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x01, 0xa4, 0x0f, 0x6d,
+ 0x00, 0xe2, 0xaa, 0x5b,
+ 0x00, 0xe2, 0x60, 0x5c,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x00, 0xe2, 0xaa, 0x5a,
+ 0xf3, 0x90, 0xd5, 0x19,
+ 0x00, 0xe2, 0x54, 0x54,
+ 0x80, 0x90, 0x55, 0x6c,
+ 0x13, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x5c, 0x44,
+ 0x04, 0x8c, 0xe1, 0x30,
+ 0x01, 0xea, 0xf2, 0x00,
+ 0x02, 0xea, 0x36, 0x00,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0x00, 0xe2, 0xd8, 0x5c,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x02, 0xa8, 0xf4, 0x31,
+ 0x02, 0xa6, 0x72, 0x7c,
+ 0x00, 0xe2, 0xd4, 0x58,
+ 0x20, 0x5b, 0xba, 0x6c,
+ 0xfc, 0x42, 0x6c, 0x7c,
+ 0x10, 0x40, 0x6e, 0x6c,
+ 0x20, 0x4d, 0x70, 0x7c,
+ 0x08, 0x5d, 0xba, 0x6c,
+ 0x02, 0xa6, 0x5e, 0x6b,
+ 0x00, 0xe2, 0xd4, 0x58,
+ 0x20, 0x5b, 0xba, 0x6c,
+ 0x01, 0x1b, 0x82, 0x6c,
+ 0xfc, 0x42, 0x7c, 0x7c,
+ 0x10, 0x40, 0x7e, 0x6c,
+ 0x20, 0x4d, 0xa0, 0x7c,
+ 0x08, 0x5d, 0xba, 0x6c,
+ 0x00, 0xe2, 0xa0, 0x44,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x08, 0xa8, 0xa3, 0x7c,
+ 0x04, 0x5d, 0x0e, 0x7d,
+ 0x01, 0x1a, 0x0e, 0x7d,
+ 0x01, 0xa4, 0x49, 0x03,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x02, 0xa8, 0x82, 0x32,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x00, 0xe2, 0xba, 0x42,
+ 0x02, 0xa8, 0x82, 0x32,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0xff, 0xea, 0xd4, 0x19,
+ 0x00, 0xe2, 0x48, 0x5b,
+ 0x00, 0xe2, 0xe8, 0x5c,
+ 0x00, 0xe2, 0xba, 0x52,
+ 0xff, 0xea, 0xd4, 0x0d,
+ 0x00, 0xe2, 0xd4, 0x58,
+ 0x40, 0x5b, 0xae, 0x6c,
+ 0x04, 0x5d, 0x0e, 0x7d,
+ 0x01, 0x1a, 0x0e, 0x7d,
+ 0x20, 0x4d, 0xa0, 0x7c,
+ 0x40, 0x5b, 0xfc, 0x7c,
+ 0x04, 0x5d, 0x0e, 0x7d,
+ 0x01, 0x1a, 0x0e, 0x7d,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x01, 0xa4, 0x49, 0x03,
+ 0x08, 0xa8, 0x95, 0x6c,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x01, 0x5b, 0x40, 0x31,
+ 0x20, 0xea, 0xb6, 0x00,
+ 0x00, 0xe2, 0x06, 0x5c,
+ 0x00, 0xe2, 0x14, 0x5b,
+ 0x00, 0xe2, 0x42, 0x5b,
+ 0x20, 0x5c, 0xb8, 0x00,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x20, 0xa0, 0xfc, 0x7c,
+ 0x08, 0xa8, 0xd1, 0x7c,
+ 0x00, 0xe2, 0x94, 0x44,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0xff, 0x6a, 0xde, 0x7c,
+ 0x14, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xde, 0x44,
+ 0x00, 0xe2, 0xd2, 0x58,
+ 0x10, 0x5d, 0xd2, 0x6c,
+ 0x40, 0x5b, 0xa0, 0x7c,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0xff, 0xea, 0x10, 0x03,
+ 0x08, 0xa8, 0x51, 0x03,
+ 0x00, 0xe2, 0x94, 0x44,
+ 0x80, 0xf9, 0xa0, 0x6c,
+ 0x01, 0x42, 0xc1, 0x31,
+ 0x00, 0xfb, 0xa0, 0x64,
+ 0x01, 0x41, 0xc1, 0x31,
+ 0x00, 0xfa, 0xa0, 0x64,
+ 0x01, 0xe8, 0xd4, 0x1d,
+ 0x30, 0x3f, 0xc0, 0x09,
+ 0x30, 0xe0, 0xa0, 0x64,
+ 0x40, 0x4b, 0xa0, 0x6c,
+ 0xff, 0xea, 0x52, 0x01,
+ 0xee, 0x00, 0x02, 0x6d,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x20, 0xea, 0x9a, 0x00,
+ 0xf3, 0x42, 0x0a, 0x6d,
+ 0x18, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0x11, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x70, 0x41,
+ 0x15, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xaa, 0x5a,
+ 0x33, 0xea, 0xda, 0x58,
+ 0x80, 0x3d, 0x7a, 0x00,
+ 0x00, 0xe2, 0x2e, 0x5d,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0x00, 0xe2, 0xd4, 0x58,
+ 0x40, 0x1a, 0x22, 0x7d,
+ 0x00, 0xe2, 0x2e, 0x5d,
+ 0x80, 0x19, 0x32, 0x00,
+ 0x40, 0x5b, 0x26, 0x6d,
+ 0x20, 0x4d, 0xa0, 0x7c,
+ 0x02, 0x84, 0x09, 0x03,
+ 0x40, 0x5b, 0xfc, 0x7c,
+ 0x08, 0xa8, 0x8d, 0x6c,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0x01, 0x37, 0xe1, 0x30,
+ 0x05, 0x38, 0xe3, 0x98,
+ 0x01, 0xe0, 0xf8, 0x31,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x00, 0x39, 0xe5, 0x20,
+ 0x00, 0x3a, 0xe7, 0x20,
+ 0x01, 0xfc, 0xc0, 0x31,
+ 0x04, 0xea, 0xe8, 0x30,
+ 0xff, 0xea, 0xf0, 0x08,
+ 0x02, 0xea, 0xf2, 0x00,
+ 0xff, 0xea, 0xf4, 0x08
+};
+
+static int aic_patch10_func(struct ahd_softc *ahd);
+
+static int
+aic_patch10_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0);
+}
+
+static int aic_patch9_func(struct ahd_softc *ahd);
+
+static int
+aic_patch9_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0);
+}
+
+static int aic_patch8_func(struct ahd_softc *ahd);
+
+static int
+aic_patch8_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_INITIATORROLE) != 0);
+}
+
+static int aic_patch7_func(struct ahd_softc *ahd);
+
+static int
+aic_patch7_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_TARGETROLE) != 0);
+}
+
+static int aic_patch6_func(struct ahd_softc *ahd);
+
+static int
+aic_patch6_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0);
+}
+
+static int aic_patch5_func(struct ahd_softc *ahd);
+
+static int
+aic_patch5_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_39BIT_ADDRESSING) != 0);
+}
+
+static int aic_patch4_func(struct ahd_softc *ahd);
+
+static int
+aic_patch4_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_64BIT_ADDRESSING) != 0);
+}
+
+static int aic_patch3_func(struct ahd_softc *ahd);
+
+static int
+aic_patch3_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0);
+}
+
+static int aic_patch2_func(struct ahd_softc *ahd);
+
+static int
+aic_patch2_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0);
+}
+
+static int aic_patch1_func(struct ahd_softc *ahd);
+
+static int
+aic_patch1_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0);
+}
+
+static int aic_patch0_func(struct ahd_softc *ahd);
+
+static int
+aic_patch0_func(struct ahd_softc *ahd)
+{
+ return (0);
+}
+
+typedef int patch_func_t (struct ahd_softc *ahd);
+static struct patch {
+ patch_func_t *patch_func;
+ uint32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
+} patches[] = {
+ { aic_patch1_func, 112, 1, 1 },
+ { aic_patch1_func, 139, 16, 2 },
+ { aic_patch0_func, 155, 1, 1 },
+ { aic_patch2_func, 175, 2, 1 },
+ { aic_patch3_func, 283, 1, 1 },
+ { aic_patch3_func, 286, 1, 1 },
+ { aic_patch3_func, 288, 1, 1 },
+ { aic_patch3_func, 320, 1, 1 },
+ { aic_patch4_func, 445, 1, 2 },
+ { aic_patch0_func, 446, 1, 1 },
+ { aic_patch5_func, 451, 1, 1 },
+ { aic_patch4_func, 452, 1, 1 },
+ { aic_patch6_func, 504, 1, 1 },
+ { aic_patch7_func, 509, 1, 1 },
+ { aic_patch8_func, 510, 2, 1 },
+ { aic_patch7_func, 513, 1, 2 },
+ { aic_patch0_func, 514, 1, 1 },
+ { aic_patch6_func, 577, 1, 1 },
+ { aic_patch6_func, 605, 1, 1 },
+ { aic_patch6_func, 623, 1, 1 },
+ { aic_patch9_func, 649, 2, 2 },
+ { aic_patch0_func, 651, 2, 1 },
+ { aic_patch10_func, 654, 3, 1 },
+ { aic_patch10_func, 663, 11, 1 }
+};
+static struct cs {
+ u_int16_t begin;
+ u_int16_t end;
+} critical_sections[] = {
+ { 4, 5 },
+ { 6, 7 },
+ { 13, 19 },
+ { 20, 30 },
+ { 75, 102 },
+ { 105, 109 },
+ { 112, 120 },
+ { 139, 172 },
+ { 577, 594 },
+ { 599, 605 },
+ { 605, 617 },
+ { 623, 628 },
+ { 628, 634 }
+};
+static const int num_critical_sections = sizeof(critical_sections)
+ / sizeof(*critical_sections);
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx.h b/xen/drivers/scsi/aic7xxx/aic7xxx.h
new file mode 100644
index 0000000000..289e0bf44e
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx.h
@@ -0,0 +1,1245 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.h#45 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.h,v 1.16.2.13 2002/04/29 19:36:29 gibbs Exp $
+ */
+
+#ifndef _AIC7XXX_H_
+#define _AIC7XXX_H_
+
+/* Register Definitions */
+#include "aic7xxx_reg.h"
+
+/************************* Forward Declarations *******************************/
+struct ahc_platform_data;
+struct scb_platform_data;
+struct seeprom_descriptor;
+
+/****************************** Useful Macros *********************************/
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD (~0)
+
+#define SCSIID_TARGET(ahc, scsiid) \
+ (((scsiid) & ((((ahc)->features & AHC_TWIN) != 0) ? TWIN_TID : TID)) \
+ >> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid) \
+ ((scsiid) & OID)
+#define SCSIID_CHANNEL(ahc, scsiid) \
+ ((((ahc)->features & AHC_TWIN) != 0) \
+ ? ((((scsiid) & TWIN_CHNLB) != 0) ? 'B' : 'A') \
+ : 'A')
+#define SCB_IS_SCSIBUS_B(ahc, scb) \
+ (SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid) == 'B')
+#define SCB_GET_OUR_ID(scb) \
+ SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define SCB_GET_TARGET(ahc, scb) \
+ SCSIID_TARGET((ahc), (scb)->hscb->scsiid)
+#define SCB_GET_CHANNEL(ahc, scb) \
+ SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid)
+#define SCB_GET_LUN(scb) \
+ ((scb)->hscb->lun)
+#define SCB_GET_TARGET_OFFSET(ahc, scb) \
+ (SCB_GET_TARGET(ahc, scb) + (SCB_IS_SCSIBUS_B(ahc, scb) ? 8 : 0))
+#define SCB_GET_TARGET_MASK(ahc, scb) \
+ (0x01 << (SCB_GET_TARGET_OFFSET(ahc, scb)))
+#define TCL_TARGET_OFFSET(tcl) \
+ ((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+ (tcl & (AHC_NUM_LUNS - 1))
+#define BUILD_TCL(scsiid, lun) \
+ ((lun) | (((scsiid) & TID) << 4))
+
+#ifndef AHC_TARGET_MODE
+#undef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHC_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in SPI3.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but it is doubtful this driver will ever support IUTs.
+ */
+#define AHC_NUM_LUNS 64
+
+/*
+ * The maximum transfer per S/G segment.
+ */
+#define AHC_MAXTRANSFER_SIZE 0x00ffffff /* limited by 24bit counter */
+
+/*
+ * The maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound. Controllers vary in the number
+ * they actually support.
+ */
+#define AHC_SCB_MAX 255
+
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information. Although
+ * the space for SCBs on the host adapter varies by model, the driver will
+ * page the SCBs between host and controller memory as needed. We are limited
+ * to 253 because:
+ * 1) The 8bit nature of the RISC engine holds us to an 8bit value.
+ * 2) We reserve one value, 255, to represent the invalid element.
+ * 3) Our input queue scheme requires one SCB to always be reserved
+ * in advance of queuing any SCBs. This takes us down to 254.
+ * 4) To handle our output queue correctly on machines that only
+ * support 32bit stores, we must clear the array 4 bytes at a
+ * time. To avoid colliding with a DMA write from the sequencer,
+ * we must be sure that 4 slots are empty when we write to clear
+ * the queue. This reduces us to 253 SCBs: 1 that just completed
+ * and the known three additional empty slots in the queue that
+ * precede it.
+ */
+#define AHC_MAX_QUEUE 253
+
+/*
+ * The maximum amount of SCB storage we allocate in host memory. This
+ * number should reflect the 1 additional SCB we require to handle our
+ * qinfifo mechanism.
+ */
+#define AHC_SCB_MAX_ALLOC (AHC_MAX_QUEUE+1)
+
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHC_TMODE_CMDS 256
+
+/* Reset line assertion time in us */
+#define AHC_BUSRESET_DELAY 250
+
+/******************* Chip Characteristics/Operating Settings *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
+typedef enum {
+ AHC_NONE = 0x0000,
+ AHC_CHIPID_MASK = 0x00FF,
+ AHC_AIC7770 = 0x0001,
+ AHC_AIC7850 = 0x0002,
+ AHC_AIC7855 = 0x0003,
+ AHC_AIC7859 = 0x0004,
+ AHC_AIC7860 = 0x0005,
+ AHC_AIC7870 = 0x0006,
+ AHC_AIC7880 = 0x0007,
+ AHC_AIC7895 = 0x0008,
+ AHC_AIC7895C = 0x0009,
+ AHC_AIC7890 = 0x000a,
+ AHC_AIC7896 = 0x000b,
+ AHC_AIC7892 = 0x000c,
+ AHC_AIC7899 = 0x000d,
+ AHC_VL = 0x0100, /* Bus type VL */
+ AHC_EISA = 0x0200, /* Bus type EISA */
+ AHC_PCI = 0x0400, /* Bus type PCI */
+ AHC_BUS_MASK = 0x0F00
+} ahc_chip;
+
+/*
+ * Features available in each chip type.
+ */
+typedef enum {
+ AHC_FENONE = 0x00000,
+ AHC_ULTRA = 0x00001, /* Supports 20MHz Transfers */
+ AHC_ULTRA2 = 0x00002, /* Supports 40MHz Transfers */
+ AHC_WIDE = 0x00004, /* Wide Channel */
+ AHC_TWIN = 0x00008, /* Twin Channel */
+ AHC_MORE_SRAM = 0x00010, /* 80 bytes instead of 64 */
+ AHC_CMD_CHAN = 0x00020, /* Has a Command DMA Channel */
+ AHC_QUEUE_REGS = 0x00040, /* Has Queue management registers */
+ AHC_SG_PRELOAD = 0x00080, /* Can perform auto-SG preload */
+ AHC_SPIOCAP = 0x00100, /* Has a Serial Port I/O Cap Register */
+ AHC_MULTI_TID = 0x00200, /* Has bitmask of TIDs for select-in */
+ AHC_HS_MAILBOX = 0x00400, /* Has HS_MAILBOX register */
+ AHC_DT = 0x00800, /* Double Transition transfers */
+ AHC_NEW_TERMCTL = 0x01000, /* Newer termination scheme */
+ AHC_MULTI_FUNC = 0x02000, /* Multi-Function Twin Channel Device */
+ AHC_LARGE_SCBS = 0x04000, /* 64byte SCBs */
+ AHC_AUTORATE = 0x08000, /* Automatic update of SCSIRATE/OFFSET*/
+ AHC_AUTOPAUSE = 0x10000, /* Automatic pause on register access */
+ AHC_TARGETMODE = 0x20000, /* Has tested target mode support */
+ AHC_MULTIROLE = 0x40000, /* Space for two roles at a time */
+ AHC_REMOVABLE = 0x80000, /* Hot-Swap supported */
+ AHC_AIC7770_FE = AHC_FENONE,
+ /*
+ * The real 7850 does not support Ultra modes, but there are
+ * several cards that use the generic 7850 PCI ID even though
+ * they are using an Ultra capable chip (7859/7860). We start
+ * out with the AHC_ULTRA feature set and then check the DEVSTATUS
+ * register to determine if the capability is really present.
+ */
+ AHC_AIC7850_FE = AHC_SPIOCAP|AHC_AUTOPAUSE|AHC_TARGETMODE|AHC_ULTRA,
+ AHC_AIC7860_FE = AHC_AIC7850_FE,
+ AHC_AIC7870_FE = AHC_TARGETMODE,
+ AHC_AIC7880_FE = AHC_AIC7870_FE|AHC_ULTRA,
+ /*
+ * Although we have space for both the initiator and
+ * target roles on ULTRA2 chips, we currently disable
+ * the initiator role to allow multi-scsi-id target mode
+ * configurations. We can only respond on the same SCSI
+ * ID as our initiator role if we allow initiator operation.
+ * At some point, we should add a configuration knob to
+ * allow both roles to be loaded.
+ */
+ AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2
+ |AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_MULTI_TID
+ |AHC_HS_MAILBOX|AHC_NEW_TERMCTL|AHC_LARGE_SCBS
+ |AHC_TARGETMODE,
+ AHC_AIC7892_FE = AHC_AIC7890_FE|AHC_DT|AHC_AUTORATE|AHC_AUTOPAUSE,
+ AHC_AIC7895_FE = AHC_AIC7880_FE|AHC_MORE_SRAM|AHC_AUTOPAUSE
+ |AHC_CMD_CHAN|AHC_MULTI_FUNC|AHC_LARGE_SCBS,
+ AHC_AIC7895C_FE = AHC_AIC7895_FE|AHC_MULTI_TID,
+ AHC_AIC7896_FE = AHC_AIC7890_FE|AHC_MULTI_FUNC,
+ AHC_AIC7899_FE = AHC_AIC7892_FE|AHC_MULTI_FUNC
+} ahc_feature;
+
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+ AHC_BUGNONE = 0x00,
+ /*
+ * On all chips prior to the U2 product line,
+ * the WIDEODD S/G segment feature does not
+ * work during scsi->HostBus transfers.
+ */
+ AHC_TMODE_WIDEODD_BUG = 0x01,
+ /*
+ * On the aic7890/91 Rev 0 chips, the autoflush
+ * feature does not work. A manual flush of
+ * the DMA FIFO is required.
+ */
+ AHC_AUTOFLUSH_BUG = 0x02,
+ /*
+ * On many chips, cacheline streaming does not work.
+ */
+ AHC_CACHETHEN_BUG = 0x04,
+ /*
+ * On the aic7896/97 chips, cacheline
+ * streaming must be enabled.
+ */
+ AHC_CACHETHEN_DIS_BUG = 0x08,
+ /*
+ * PCI 2.1 Retry failure on non-empty data fifo.
+ */
+ AHC_PCI_2_1_RETRY_BUG = 0x10,
+ /*
+ * Controller does not handle cacheline residuals
+ * properly on S/G segments if PCI MWI instructions
+ * are allowed.
+ */
+ AHC_PCI_MWI_BUG = 0x20,
+ /*
+ * An SCB upload using the SCB channel's
+ * auto array entry copy feature may
+ * corrupt data. This appears to only
+ * occur on 66MHz systems.
+ */
+ AHC_SCBCHAN_UPLOAD_BUG = 0x40
+} ahc_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
+typedef enum {
+ AHC_FNONE = 0x000,
+ AHC_PRIMARY_CHANNEL = 0x003,/*
+ * The channel that should
+ * be probed first.
+ */
+ AHC_USEDEFAULTS = 0x004,/*
+ * For cards without an seeprom
+ * or a BIOS to initialize the chip's
+ * SRAM, we use the default target
+ * settings.
+ */
+ AHC_SEQUENCER_DEBUG = 0x008,
+ AHC_SHARED_SRAM = 0x010,
+ AHC_LARGE_SEEPROM = 0x020,/* Uses C56_66 not C46 */
+ AHC_RESET_BUS_A = 0x040,
+ AHC_RESET_BUS_B = 0x080,
+ AHC_EXTENDED_TRANS_A = 0x100,
+ AHC_EXTENDED_TRANS_B = 0x200,
+ AHC_TERM_ENB_A = 0x400,
+ AHC_TERM_ENB_B = 0x800,
+ AHC_INITIATORROLE = 0x1000,/*
+ * Allow initiator operations on
+ * this controller.
+ */
+ AHC_TARGETROLE = 0x2000,/*
+ * Allow target operations on this
+ * controller.
+ */
+ AHC_NEWEEPROM_FMT = 0x4000,
+ AHC_RESOURCE_SHORTAGE = 0x8000,
+ AHC_TQINFIFO_BLOCKED = 0x10000,/* Blocked waiting for ATIOs */
+ AHC_INT50_SPEEDFLEX = 0x20000,/*
+ * Internal 50pin connector
+ * sits behind an aic3860
+ */
+ AHC_SCB_BTT = 0x40000,/*
+ * The busy targets table is
+ * stored in SCB space rather
+ * than SRAM.
+ */
+ AHC_BIOS_ENABLED = 0x80000,
+ AHC_ALL_INTERRUPTS = 0x100000,
+ AHC_PAGESCBS = 0x400000, /* Enable SCB paging */
+ AHC_EDGE_INTERRUPT = 0x800000, /* Device uses edge triggered ints */
+ AHC_39BIT_ADDRESSING = 0x1000000, /* Use 39 bit addressing scheme. */
+ AHC_LSCBS_ENABLED = 0x2000000, /* 64Byte SCBs enabled */
+ AHC_SCB_CONFIG_USED = 0x4000000 /* No SEEPROM but SCB2 had info. */
+} ahc_flag;
+
+/************************* Hardware SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory. The SCB
+ * consists of a "hardware SCB" mirroring the fields availible on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the cdb) to be executed. After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role. See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target. The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally (non-zero status byte).
+ */
+struct status_pkt {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sg_ptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* Standard SCSI status byte */
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sg_ptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* SCSI status to give to initiator */
+ uint8_t target_phases; /* Bitmap of phases to execute */
+ uint8_t data_phase; /* Data-In or Data-Out */
+ uint8_t initiator_tag; /* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/ union {
+ /*
+ * If the cdb is 12 bytes or less, we embed it directly
+ * in the SCB. For longer cdbs, we embed the address
+ * of the cdb payload as seen by the chip and a DMA
+ * is used to pull it in.
+ */
+ uint8_t cdb[12];
+ uint32_t cdb_ptr;
+ struct status_pkt status;
+ struct target_data tdata;
+ } shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer. The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag. Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields). If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur. Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields. After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced. After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set. If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete. If the
+ * sequencer determines that there is a residual in the tranfer, it
+ * will set the SG_RESID_VALID flag in sgptr and dma the scb back into
+ * host memory. To sumarize:
+ *
+ * Sequencer:
+ * o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ * or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ * o We are transfering the last segment if residual_datacnt has
+ * the SG_LAST_SEG flag set.
+ *
+ * Host:
+ * o A residual has occurred if a completed scb has the
+ * SG_RESID_VALID flag set.
+ *
+ * o residual_sgptr and sgptr refer to the "next" sg entry
+ * and so may point beyond the last valid sg entry for the
+ * transfer.
+ */
+/*12*/ uint32_t dataptr;
+/*16*/ uint32_t datacnt; /*
+ * Byte 3 (numbered from 0) of
+ * the datacnt is really the
+ * 4th byte in that data address.
+ */
+/*20*/ uint32_t sgptr;
+#define SG_PTR_MASK 0xFFFFFFF8
+/*24*/ uint8_t control; /* See SCB_CONTROL in aic7xxx.reg for details */
+/*25*/ uint8_t scsiid; /* what to load in the SCSIID register */
+/*26*/ uint8_t lun;
+/*27*/ uint8_t tag; /*
+ * Index into our kernel SCB array.
+ * Also used as the tag for tagged I/O
+ */
+/*28*/ uint8_t cdb_len;
+/*29*/ uint8_t scsirate; /* Value for SCSIRATE register */
+/*30*/ uint8_t scsioffset; /* Value for SCSIOFFSET register */
+/*31*/ uint8_t next; /*
+ * Used for threading SCBs in the
+ * "Waiting for Selection" and
+ * "Disconnected SCB" lists down
+ * in the sequencer.
+ */
+/*32*/ uint8_t cdb32[32]; /*
+ * CDB storage for cdbs of size
+ * 13->32. We store them here
+ * because hardware scbs are
+ * allocated from DMA safe
+ * memory so we are guaranteed
+ * the controller can access
+ * this data.
+ */
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent. Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transfered to the controller.
+ * The aic7xxx chips only support a 24bit length. We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer. This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahc_dma_seg {
+ uint32_t addr;
+ uint32_t len;
+#define AHC_DMA_LAST_SEG 0x80000000
+#define AHC_SG_HIGH_ADDR_MASK 0x7F000000
+#define AHC_SG_LEN_MASK 0x00FFFFFF
+};
+
+struct sg_map_node {
+ bus_dmamap_t sg_dmamap;
+ bus_addr_t sg_physaddr;
+ struct ahc_dma_seg* sg_vaddr;
+ SLIST_ENTRY(sg_map_node) links;
+};
+
+/*
+ * The current state of this SCB.
+ */
+typedef enum {
+ SCB_FREE = 0x0000,
+ SCB_OTHERTCL_TIMEOUT = 0x0002,/*
+ * Another device was active
+ * during the first timeout for
+ * this SCB so we gave ourselves
+ * an additional timeout period
+ * in case it was hogging the
+ * bus.
+ */
+ SCB_DEVICE_RESET = 0x0004,
+ SCB_SENSE = 0x0008,
+ SCB_CDB32_PTR = 0x0010,
+ SCB_RECOVERY_SCB = 0x0020,
+ SCB_AUTO_NEGOTIATE = 0x0040,/* Negotiate to achieve goal. */
+ SCB_NEGOTIATE = 0x0080,/* Negotiation forced for command. */
+ SCB_ABORT = 0x1000,
+ SCB_UNTAGGEDQ = 0x2000,
+ SCB_ACTIVE = 0x4000,
+ SCB_TARGET_IMMEDIATE = 0x8000
+} scb_flag;
+
+struct scb {
+ struct hardware_scb *hscb;
+ union {
+ SLIST_ENTRY(scb) sle;
+ TAILQ_ENTRY(scb) tqe;
+ } links;
+ LIST_ENTRY(scb) pending_links;
+ ahc_io_ctx_t io_ctx;
+ struct ahc_softc *ahc_softc;
+ scb_flag flags;
+#ifndef __linux__
+ bus_dmamap_t dmamap;
+#endif
+ struct scb_platform_data *platform_data;
+ struct sg_map_node *sg_map;
+ struct ahc_dma_seg *sg_list;
+ bus_addr_t sg_list_phys;
+ u_int sg_count;/* How full ahc_dma_seg is */
+};
+
+struct scb_data {
+ SLIST_HEAD(, scb) free_scbs; /*
+ * Pool of SCBs ready to be assigned
+ * commands to execute.
+ */
+ struct scb *scbindex[256]; /*
+ * Mapping from tag to SCB.
+ * As tag identifiers are an
+ * 8bit value, we provide space
+ * for all possible tag values.
+ * Any lookups to entries at or
+ * above AHC_SCB_MAX_ALLOC will
+ * always fail.
+ */
+ struct hardware_scb *hscbs; /* Array of hardware SCBs */
+ struct scb *scbarray; /* Array of kernel SCBs */
+ struct scsi_sense_data *sense; /* Per SCB sense data */
+
+ /*
+ * "Bus" addresses of our data structures.
+ */
+ bus_dma_tag_t hscb_dmat; /* dmat for our hardware SCB array */
+ bus_dmamap_t hscb_dmamap;
+ bus_addr_t hscb_busaddr;
+ bus_dma_tag_t sense_dmat;
+ bus_dmamap_t sense_dmamap;
+ bus_addr_t sense_busaddr;
+ bus_dma_tag_t sg_dmat; /* dmat for our sg segments */
+ SLIST_HEAD(, sg_map_node) sg_maps;
+ uint8_t numscbs;
+ uint8_t maxhscbs; /* Number of SCBs on the card */
+ uint8_t init_level; /*
+ * How far we've initialized
+ * this structure.
+ */
+};
+
+/************************ Target Mode Definitions *****************************/
+
+/*
+ * Connection desciptor for select-in requests in target mode.
+ */
+struct target_cmd {
+ uint8_t scsiid; /* Our ID and the initiator's ID */
+ uint8_t identify; /* Identify message */
+ uint8_t bytes[22]; /*
+ * Bytes contains any additional message
+ * bytes terminated by 0xFF. The remainder
+ * is the cdb to execute.
+ */
+ uint8_t cmd_valid; /*
+ * When a command is complete, the firmware
+ * will set cmd_valid to all bits set.
+ * After the host has seen the command,
+ * the bits are cleared. This allows us
+ * to just peek at host memory to determine
+ * if more work is complete. cmd_valid is on
+ * an 8 byte boundary to simplify setting
+ * it on aic7880 hardware which only has
+ * limited direct access to the DMA FIFO.
+ */
+ uint8_t pad[7];
+};
+
+/*
+ * Number of events we can buffer up if we run out
+ * of immediate notify ccbs.
+ */
+#define AHC_TMODE_EVENT_BUFFER_SIZE 8
+struct ahc_tmode_event {
+ uint8_t initiator_id;
+ uint8_t event_type; /* MSG type or EVENT_TYPE_BUS_RESET */
+#define EVENT_TYPE_BUS_RESET 0xFF
+ uint8_t event_arg;
+};
+
+/*
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it. Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
+ */
+#ifdef AHC_TARGET_MODE
+struct ahc_tmode_lstate {
+ struct cam_path *path;
+ struct ccb_hdr_slist accept_tios;
+ struct ccb_hdr_slist immed_notifies;
+ struct ahc_tmode_event event_buffer[AHC_TMODE_EVENT_BUFFER_SIZE];
+ uint8_t event_r_idx;
+ uint8_t event_w_idx;
+};
+#else
+struct ahc_tmode_lstate;
+#endif
+
+/******************** Transfer Negotiation Datastructures *********************/
+#define AHC_TRANS_CUR 0x01 /* Modify current neogtiation status */
+#define AHC_TRANS_ACTIVE 0x03 /* Assume this target is on the bus */
+#define AHC_TRANS_GOAL 0x04 /* Modify negotiation goal */
+#define AHC_TRANS_USER 0x08 /* Modify user negotiation settings */
+
+/*
+ * Transfer Negotiation Information.
+ */
+struct ahc_transinfo {
+ uint8_t protocol_version; /* SCSI Revision level */
+ uint8_t transport_version; /* SPI Revision level */
+ uint8_t width; /* Bus width */
+ uint8_t period; /* Sync rate factor */
+ uint8_t offset; /* Sync offset */
+ uint8_t ppr_options; /* Parallel Protocol Request options */
+};
+
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
+struct ahc_initiator_tinfo {
+ uint8_t scsirate; /* Computed value for SCSIRATE reg */
+ struct ahc_transinfo curr;
+ struct ahc_transinfo goal;
+ struct ahc_transinfo user;
+};
+
+/*
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping. For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
+ */
+struct ahc_tmode_tstate {
+ struct ahc_tmode_lstate* enabled_luns[AHC_NUM_LUNS];
+ struct ahc_initiator_tinfo transinfo[AHC_NUM_TARGETS];
+
+ /*
+ * Per initiator state bitmasks.
+ */
+ uint16_t auto_negotiate;/* Auto Negotiation Required */
+ uint16_t ultraenb; /* Using ultra sync rate */
+ uint16_t discenable; /* Disconnection allowed */
+ uint16_t tagenable; /* Tagged Queuing allowed */
+};
+
+/*
+ * Data structure for our table of allowed synchronous transfer rates.
+ */
+struct ahc_syncrate {
+ u_int sxfr_u2; /* Value of the SXFR parameter for Ultra2+ Chips */
+ u_int sxfr; /* Value of the SXFR parameter for <= Ultra Chips */
+#define ULTRA_SXFR 0x100 /* Rate Requires Ultra Mode set */
+#define ST_SXFR 0x010 /* Rate Single Transition Only */
+#define DT_SXFR 0x040 /* Rate Double Transition Only */
+ uint8_t period; /* Period to send to SCSI target */
+ char *rate;
+};
+
+/*
+ * The synchronouse transfer rate table.
+ */
+extern struct ahc_syncrate ahc_syncrates[];
+
+/*
+ * Indexes into our table of syncronous transfer rates.
+ */
+#define AHC_SYNCRATE_DT 0
+#define AHC_SYNCRATE_ULTRA2 1
+#define AHC_SYNCRATE_ULTRA 3
+#define AHC_SYNCRATE_FAST 6
+
+/***************************** Lookup Tables **********************************/
+/*
+ * Phase -> name and message out response
+ * to parity errors in each phase table.
+ */
+struct ahc_phase_table_entry {
+ uint8_t phase;
+ uint8_t mesg_out; /* Message response to parity errors */
+ char *phasemsg;
+};
+
+/************************** Serial EEPROM Format ******************************/
+
+struct seeprom_config {
+/*
+ * Per SCSI ID Configuration Flags
+ */
+ uint16_t device_flags[16]; /* words 0-15 */
+#define CFXFER 0x0007 /* synchronous transfer rate */
+#define CFSYNCH 0x0008 /* enable synchronous transfer */
+#define CFDISC 0x0010 /* enable disconnection */
+#define CFWIDEB 0x0020 /* wide bus device */
+#define CFSYNCHISULTRA 0x0040 /* CFSYNCH is an ultra offset (2940AU)*/
+#define CFSYNCSINGLE 0x0080 /* Single-Transition signalling */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFRNFOUND 0x0400 /* report even if not found */
+#define CFMULTILUNDEV 0x0800 /* Probe multiple luns in BIOS scan */
+#define CFWBCACHEENB 0x4000 /* Enable W-Behind Cache on disks */
+#define CFWBCACHENOP 0xc000 /* Don't touch W-Behind Cache */
+
+/*
+ * BIOS Control Bits
+ */
+ uint16_t bios_control; /* word 16 */
+#define CFSUPREM 0x0001 /* support all removeable drives */
+#define CFSUPREMB 0x0002 /* support removeable boot drives */
+#define CFBIOSEN 0x0004 /* BIOS enabled */
+#define CFBIOS_BUSSCAN 0x0008 /* Have the BIOS Scan the Bus */
+#define CFSM2DRV 0x0010 /* support more than two drives */
+#define CFSTPWLEVEL 0x0010 /* Termination level control */
+#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
+#define CFCTRL_A 0x0020 /* BIOS displays Ctrl-A message */
+#define CFTERM_MENU 0x0040 /* BIOS displays termination menu */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+#define CFSCAMEN 0x0100 /* SCAM enable */
+#define CFMSG_LEVEL 0x0600 /* BIOS Message Level */
+#define CFMSG_VERBOSE 0x0000
+#define CFMSG_SILENT 0x0200
+#define CFMSG_DIAG 0x0400
+#define CFBOOTCD 0x0800 /* Support Bootable CD-ROM */
+/* UNUSED 0xff00 */
+
+/*
+ * Host Adapter Control Bits
+ */
+ uint16_t adapter_control; /* word 17 */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
+#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable */
+#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
+#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination */
+#define CFWSTERM 0x0008 /* SCSI high byte termination */
+#define CFSPARITY 0x0010 /* SCSI parity */
+#define CF284XSTERM 0x0020 /* SCSI low byte term (284x cards) */
+#define CFMULTILUN 0x0020
+#define CFRESETB 0x0040 /* reset SCSI bus at boot */
+#define CFCLUSTERENB 0x0080 /* Cluster Enable */
+#define CFBOOTCHAN 0x0300 /* probe this channel first */
+#define CFBOOTCHANSHIFT 8
+#define CFSEAUTOTERM 0x0400 /* Ultra2 Perform secondary Auto Term*/
+#define CFSELOWTERM 0x0800 /* Ultra2 secondary low term */
+#define CFSEHIGHTERM 0x1000 /* Ultra2 secondary high term */
+#define CFDOMAINVAL 0x4000 /* Perform Domain Validation*/
+
+/*
+ * Bus Release Time, Host Adapter ID
+ */
+ uint16_t brtime_id; /* word 18 */
+#define CFSCSIID 0x000f /* host adapter SCSI ID */
+/* UNUSED 0x00f0 */
+#define CFBRTIME 0xff00 /* bus release time */
+
+/*
+ * Maximum targets
+ */
+ uint16_t max_targets; /* word 19 */
+#define CFMAXTARG 0x00ff /* maximum targets */
+#define CFBOOTLUN 0x0f00 /* Lun to boot from */
+#define CFBOOTID 0xf000 /* Target to boot from */
+ uint16_t res_1[10]; /* words 20-29 */
+ uint16_t signature; /* Signature == 0x250 */
+#define CFSIGNATURE 0x250
+#define CFSIGNATURE2 0x300
+ uint16_t checksum; /* word 31 */
+};
+
+/**************************** Message Buffer *********************************/
+typedef enum {
+ MSG_TYPE_NONE = 0x00,
+ MSG_TYPE_INITIATOR_MSGOUT = 0x01,
+ MSG_TYPE_INITIATOR_MSGIN = 0x02,
+ MSG_TYPE_TARGET_MSGOUT = 0x03,
+ MSG_TYPE_TARGET_MSGIN = 0x04
+} ahc_msg_type;
+
+typedef enum {
+ MSGLOOP_IN_PROG,
+ MSGLOOP_MSGCOMPLETE,
+ MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+/*********************** Software Configuration Structure *********************/
+TAILQ_HEAD(scb_tailq, scb);
+
+struct ahc_suspend_channel_state {
+ uint8_t scsiseq;
+ uint8_t sxfrctl0;
+ uint8_t sxfrctl1;
+ uint8_t simode0;
+ uint8_t simode1;
+ uint8_t seltimer;
+ uint8_t seqctl;
+};
+
+struct ahc_suspend_state {
+ struct ahc_suspend_channel_state channel[2];
+ uint8_t optionmode;
+ uint8_t dscommand0;
+ uint8_t dspcistatus;
+ /* hsmailbox */
+ uint8_t crccontrol1;
+ uint8_t scbbaddr;
+ /* Host and sequencer SCB counts */
+ uint8_t dff_thrsh;
+ uint8_t *scratch_ram;
+ uint8_t *btt;
+};
+
+typedef void (*ahc_bus_intr_t)(struct ahc_softc *);
+
+struct ahc_softc {
+ bus_space_tag_t tag;
+ bus_space_handle_t bsh;
+#ifndef __linux__
+ bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */
+#endif
+ struct scb_data *scb_data;
+
+ struct scb *next_queued_scb;
+
+ /*
+ * SCBs that have been sent to the controller
+ */
+ LIST_HEAD(, scb) pending_scbs;
+
+ /*
+ * Counting lock for deferring the release of additional
+ * untagged transactions from the untagged_queues. When
+ * the lock is decremented to 0, all queues in the
+ * untagged_queues array are run.
+ */
+ u_int untagged_queue_lock;
+
+ /*
+ * Per-target queue of untagged-transactions. The
+ * transaction at the head of the queue is the
+ * currently pending untagged transaction for the
+ * target. The driver only allows a single untagged
+ * transaction per target.
+ */
+ struct scb_tailq untagged_queues[AHC_NUM_TARGETS];
+
+ /*
+ * Platform specific data.
+ */
+ struct ahc_platform_data *platform_data;
+
+ /*
+ * Platform specific device information.
+ */
+ ahc_dev_softc_t dev_softc;
+
+ /*
+ * Bus specific device information.
+ */
+ ahc_bus_intr_t bus_intr;
+
+ /*
+ * Target mode related state kept on a per enabled lun basis.
+ * Targets that are not enabled will have null entries.
+ * As an initiator, we keep one target entry for our initiator
+ * ID to store our sync/wide transfer settings.
+ */
+ struct ahc_tmode_tstate *enabled_targets[AHC_NUM_TARGETS];
+
+ /*
+ * The black hole device responsible for handling requests for
+ * disabled luns on enabled targets.
+ */
+ struct ahc_tmode_lstate *black_hole;
+
+ /*
+ * Device instance currently on the bus awaiting a continue TIO
+ * for a command that was not given the disconnect priveledge.
+ */
+ struct ahc_tmode_lstate *pending_device;
+
+ /*
+ * Card characteristics
+ */
+ ahc_chip chip;
+ ahc_feature features;
+ ahc_bug bugs;
+ ahc_flag flags;
+ struct seeprom_config *seep_config;
+
+ /* Values to store in the SEQCTL register for pause and unpause */
+ uint8_t unpause;
+ uint8_t pause;
+
+ /* Command Queues */
+ uint8_t qoutfifonext;
+ uint8_t qinfifonext;
+ uint8_t *qoutfifo;
+ uint8_t *qinfifo;
+
+ /* Critical Section Data */
+ struct cs *critical_sections;
+ u_int num_critical_sections;
+
+ /* Links for chaining softcs */
+ TAILQ_ENTRY(ahc_softc) links;
+
+ /* Channel Names ('A', 'B', etc.) */
+ char channel;
+ char channel_b;
+
+ /* Initiator Bus ID */
+ uint8_t our_id;
+ uint8_t our_id_b;
+
+ /*
+ * PCI error detection.
+ */
+ int unsolicited_ints;
+
+ /*
+ * Target incoming command FIFO.
+ */
+ struct target_cmd *targetcmds;
+ uint8_t tqinfifonext;
+
+ /*
+ * Incoming and outgoing message handling.
+ */
+ uint8_t send_msg_perror;
+ ahc_msg_type msg_type;
+ uint8_t msgout_buf[12];/* Message we are sending */
+ uint8_t msgin_buf[12];/* Message we are receiving */
+ u_int msgout_len; /* Length of message to send */
+ u_int msgout_index; /* Current index in msgout */
+ u_int msgin_index; /* Current index in msgin */
+
+ /*
+ * Mapping information for data structures shared
+ * between the sequencer and kernel.
+ */
+ bus_dma_tag_t parent_dmat;
+ bus_dma_tag_t shared_data_dmat;
+ bus_dmamap_t shared_data_dmamap;
+ bus_addr_t shared_data_busaddr;
+
+ /*
+ * Bus address of the one byte buffer used to
+ * work-around a DMA bug for chips <= aic7880
+ * in target mode.
+ */
+ bus_addr_t dma_bug_buf;
+
+ /* Information saved through suspend/resume cycles */
+ struct ahc_suspend_state suspend_state;
+
+ /* Number of enabled target mode device on this card */
+ u_int enabled_luns;
+
+ /* Initialization level of this data structure */
+ u_int init_level;
+
+ /* PCI cacheline size. */
+ u_int pci_cachesize;
+
+ /* Per-Unit descriptive information */
+ const char *description;
+ char *name;
+ int unit;
+
+ /* Selection Timer settings */
+ int seltime;
+ int seltime_b;
+
+ uint16_t user_discenable;/* Disconnection allowed */
+ uint16_t user_tagenable;/* Tagged Queuing allowed */
+};
+
+TAILQ_HEAD(ahc_softc_tailq, ahc_softc);
+extern struct ahc_softc_tailq ahc_tailq;
+
+/************************ Active Device Information ***************************/
+typedef enum {
+ ROLE_UNKNOWN,
+ ROLE_INITIATOR,
+ ROLE_TARGET
+} role_t;
+
+struct ahc_devinfo {
+ int our_scsiid;
+ int target_offset;
+ uint16_t target_mask;
+ u_int target;
+ u_int lun;
+ char channel;
+ role_t role; /*
+ * Only guaranteed to be correct if not
+ * in the busfree state.
+ */
+};
+
+/****************************** PCI Structures ********************************/
+typedef int (ahc_device_setup_t)(struct ahc_softc *);
+
+struct ahc_pci_identity {
+ uint64_t full_id;
+ uint64_t id_mask;
+ char *name;
+ ahc_device_setup_t *setup;
+};
+extern struct ahc_pci_identity ahc_pci_ident_table [];
+extern const u_int ahc_num_pci_devs;
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+ uint32_t full_id;
+ uint32_t id_mask;
+ char *name;
+ ahc_device_setup_t *setup;
+};
+extern struct aic7770_identity aic7770_ident_table [];
+extern const int ahc_num_aic7770_devs;
+
+#define AHC_EISA_SLOT_OFFSET 0xc00
+#define AHC_EISA_IOSIZE 0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+u_int ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl);
+void ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl);
+void ahc_busy_tcl(struct ahc_softc *ahc,
+ u_int tcl, u_int busyid);
+
+/***************************** PCI Front End *********************************/
+struct ahc_pci_identity *ahc_find_pci_device(ahc_dev_softc_t);
+int ahc_pci_config(struct ahc_softc *,
+ struct ahc_pci_identity *);
+
+/*************************** EISA/VL Front End ********************************/
+struct aic7770_identity *aic7770_find_device(uint32_t);
+int aic7770_config(struct ahc_softc *ahc,
+ struct aic7770_identity *,
+ u_int port);
+
+/************************** SCB and SCB queue management **********************/
+int ahc_probe_scbs(struct ahc_softc *);
+void ahc_run_untagged_queues(struct ahc_softc *ahc);
+void ahc_run_untagged_queue(struct ahc_softc *ahc,
+ struct scb_tailq *queue);
+void ahc_qinfifo_requeue_tail(struct ahc_softc *ahc,
+ struct scb *scb);
+int ahc_match_scb(struct ahc_softc *ahc, struct scb *scb,
+ int target, char channel, int lun,
+ u_int tag, role_t role);
+
+/****************************** Initialization ********************************/
+struct ahc_softc *ahc_alloc(void *platform_arg, char *name);
+int ahc_softc_init(struct ahc_softc *);
+void ahc_controller_info(struct ahc_softc *ahc, char *buf);
+int ahc_init(struct ahc_softc *ahc);
+void ahc_intr_enable(struct ahc_softc *ahc, int enable);
+void ahc_pause_and_flushwork(struct ahc_softc *ahc);
+int ahc_suspend(struct ahc_softc *ahc);
+int ahc_resume(struct ahc_softc *ahc);
+void ahc_softc_insert(struct ahc_softc *);
+struct ahc_softc *ahc_find_softc(struct ahc_softc *ahc);
+void ahc_set_unit(struct ahc_softc *, int);
+void ahc_set_name(struct ahc_softc *, char *);
+void ahc_alloc_scbs(struct ahc_softc *ahc);
+void ahc_free(struct ahc_softc *ahc);
+int ahc_reset(struct ahc_softc *ahc);
+void ahc_shutdown(void *arg);
+
+/*************************** Interrupt Services *******************************/
+void ahc_pci_intr(struct ahc_softc *ahc);
+void ahc_clear_intstat(struct ahc_softc *ahc);
+void ahc_run_qoutfifo(struct ahc_softc *ahc);
+#ifdef AHC_TARGET_MODE
+void ahc_run_tqinfifo(struct ahc_softc *ahc, int paused);
+#endif
+void ahc_handle_brkadrint(struct ahc_softc *ahc);
+void ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat);
+void ahc_handle_scsiint(struct ahc_softc *ahc,
+ u_int intstat);
+void ahc_clear_critical_section(struct ahc_softc *ahc);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+ SEARCH_COMPLETE,
+ SEARCH_COUNT,
+ SEARCH_REMOVE
+} ahc_search_action;
+int ahc_search_qinfifo(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status,
+ ahc_search_action action);
+int ahc_search_untagged_queues(struct ahc_softc *ahc,
+ ahc_io_ctx_t ctx,
+ int target, char channel,
+ int lun, uint32_t status,
+ ahc_search_action action);
+int ahc_search_disc_list(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ int stop_on_first, int remove,
+ int save_state);
+void ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+int ahc_reset_channel(struct ahc_softc *ahc, char channel,
+ int initiate_reset);
+int ahc_abort_scbs(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+void ahc_restart(struct ahc_softc *ahc);
+void ahc_calc_residual(struct ahc_softc *ahc,
+ struct scb *scb);
+/*************************** Utility Functions ********************************/
+struct ahc_phase_table_entry*
+ ahc_lookup_phase_entry(int phase);
+void ahc_compile_devinfo(struct ahc_devinfo *devinfo,
+ u_int our_id, u_int target,
+ u_int lun, char channel,
+ role_t role);
+/************************** Transfer Negotiation ******************************/
+struct ahc_syncrate* ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+ u_int *ppr_options, u_int maxsync);
+u_int ahc_find_period(struct ahc_softc *ahc,
+ u_int scsirate, u_int maxsync);
+void ahc_validate_offset(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ struct ahc_syncrate *syncrate,
+ u_int *offset, int wide,
+ role_t role);
+void ahc_validate_width(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ u_int *bus_width,
+ role_t role);
+int ahc_update_neg_request(struct ahc_softc*,
+ struct ahc_devinfo*,
+ struct ahc_tmode_tstate*,
+ struct ahc_initiator_tinfo*,
+ int /*force*/);
+void ahc_set_width(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int width, u_int type, int paused);
+void ahc_set_syncrate(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct ahc_syncrate *syncrate,
+ u_int period, u_int offset,
+ u_int ppr_options,
+ u_int type, int paused);
+typedef enum {
+ AHC_QUEUE_NONE,
+ AHC_QUEUE_BASIC,
+ AHC_QUEUE_TAGGED
+} ahc_queue_alg;
+
+void ahc_set_tags(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ ahc_queue_alg alg);
+
+/**************************** Target Mode *************************************/
+#ifdef AHC_TARGET_MODE
+void ahc_send_lstate_events(struct ahc_softc *,
+ struct ahc_tmode_lstate *);
+void ahc_handle_en_lun(struct ahc_softc *ahc,
+ struct cam_sim *sim, union ccb *ccb);
+cam_status ahc_find_tmode_devs(struct ahc_softc *ahc,
+ struct cam_sim *sim, union ccb *ccb,
+ struct ahc_tmode_tstate **tstate,
+ struct ahc_tmode_lstate **lstate,
+ int notfound_failure);
+#ifndef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+#ifdef AHC_DEBUG
+extern int ahc_debug;
+#define AHC_SHOWMISC 0x1
+#define AHC_SHOWSENSE 0x2
+#endif
+void ahc_print_scb(struct scb *scb);
+void ahc_dump_card_state(struct ahc_softc *ahc);
+/******************************* SEEPROM *************************************/
+int ahc_acquire_seeprom(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd);
+void ahc_release_seeprom(struct seeprom_descriptor *sd);
+#endif /* _AIC7XXX_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx.reg b/xen/drivers/scsi/aic7xxx/aic7xxx.reg
new file mode 100644
index 0000000000..954038bd34
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx.reg
@@ -0,0 +1,1588 @@
+/*
+ * Aic7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.reg,v 1.20.2.11 2002/04/29 19:36:30 gibbs Exp $
+ */
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#30 $"
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic7xxx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic7xxx driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book available from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+register SCSISEQ {
+ address 0x000
+ access_mode RW
+ bit TEMODE 0x80
+ bit ENSELO 0x40
+ bit ENSELI 0x20
+ bit ENRSELI 0x10
+ bit ENAUTOATNO 0x08
+ bit ENAUTOATNI 0x04
+ bit ENAUTOATNP 0x02
+ bit SCSIRSTO 0x01
+}
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL0 {
+ address 0x001
+ access_mode RW
+ bit DFON 0x80
+ bit DFPEXP 0x40
+ bit FAST20 0x20
+ bit CLRSTCNT 0x10
+ bit SPIOEN 0x08
+ bit SCAMEN 0x04
+ bit CLRCHN 0x02
+}
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL1 {
+ address 0x002
+ access_mode RW
+ bit BITBUCKET 0x80
+ bit SWRAPEN 0x40
+ bit ENSPCHK 0x20
+ mask STIMESEL 0x18
+ bit ENSTIMER 0x04
+ bit ACTNEGEN 0x02
+ bit STPWEN 0x01 /* Powered Termination */
+}
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+register SCSISIGI {
+ address 0x003
+ access_mode RO
+ bit CDI 0x80
+ bit IOI 0x40
+ bit MSGI 0x20
+ bit ATNI 0x10
+ bit SELI 0x08
+ bit BSYI 0x04
+ bit REQI 0x02
+ bit ACKI 0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_DATAOUT_DT P_DATAOUT|MSGI
+ mask P_DATAIN_DT P_DATAIN|MSGI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Control Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus. Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+register SCSISIGO {
+ address 0x003
+ access_mode WO
+ bit CDO 0x80
+ bit IOO 0x40
+ bit MSGO 0x20
+ bit ATNO 0x10
+ bit SELO 0x08
+ bit BSYO 0x04
+ bit REQO 0x02
+ bit ACKO 0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers. Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+register SCSIRATE {
+ address 0x004
+ access_mode RW
+ bit WIDEXFER 0x80 /* Wide transfer control */
+ bit ENABLE_CRC 0x40 /* CRC for D-Phases */
+ bit SINGLE_EDGE 0x10 /* Disable DT Transfers */
+ mask SXFR 0x70 /* Sync transfer rate */
+ mask SXFR_ULTRA2 0x0f /* Sync transfer rate */
+ mask SOFS 0x0f /* Sync offset */
+}
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+register SCSIID {
+ address 0x005
+ access_mode RW
+ mask TID 0xf0 /* Target ID mask */
+ mask TWIN_TID 0x70
+ bit TWIN_CHNLB 0x80
+ mask OID 0x0f /* Our ID mask */
+ /*
+ * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
+ * The aic7890/91 allow an offset of up to 127 transfers in both wide
+ * and narrow mode.
+ */
+ alias SCSIOFFSET
+ mask SOFS_ULTRA2 0x7f /* Sync offset U2 chips */
+}
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latches used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode. SCSIDATH can be used for the
+ * upper byte of a 16bit wide asynchronouse data phase transfer.
+ */
+register SCSIDATL {
+ address 0x006
+ access_mode RW
+}
+
+register SCSIDATH {
+ address 0x007
+ access_mode RW
+}
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transferred
+ * across the SCSI bus. The counter is decremented only once
+ * the data has been safely transferred. SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */
+register STCNT {
+ address 0x008
+ size 3
+ access_mode RW
+}
+
+/* ALT_MODE register on Ultra160 chips */
+register OPTIONMODE {
+ address 0x008
+ access_mode RW
+ bit AUTORATEEN 0x80
+ bit AUTOACKEN 0x40
+ bit ATNMGMNTEN 0x20
+ bit BUSFREEREV 0x10
+ bit EXPPHASEDIS 0x08
+ bit SCSIDATL_IMGEN 0x04
+ bit AUTO_MSGOUT_DE 0x02
+ bit DIS_MSGIN_DUALEDGE 0x01
+ mask OPTIONMODE_DEFAULTS AUTO_MSGOUT_DE|DIS_MSGIN_DUALEDGE
+}
+
+/* ALT_MODE register on Ultra160 chips */
+register TARGCRCCNT {
+ address 0x00a
+ size 2
+ access_mode RW
+}
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+ address 0x00b
+ access_mode WO
+ bit CLRSELDO 0x40
+ bit CLRSELDI 0x20
+ bit CLRSELINGO 0x10
+ bit CLRSWRAP 0x08
+ bit CLRIOERR 0x08 /* Ultra2 Only */
+ bit CLRSPIORDY 0x02
+}
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+register SSTAT0 {
+ address 0x00b
+ access_mode RO
+ bit TARGET 0x80 /* Board acting as target */
+ bit SELDO 0x40 /* Selection Done */
+ bit SELDI 0x20 /* Board has been selected */
+ bit SELINGO 0x10 /* Selection In Progress */
+ bit SWRAP 0x08 /* 24bit counter wrap */
+ bit IOERR 0x08 /* LVD Tranceiver mode changed */
+ bit SDONE 0x04 /* STCNT = 0x000000 */
+ bit SPIORDY 0x02 /* SCSI PIO Ready */
+ bit DMADONE 0x01 /* DMA transfer completed */
+}
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+ address 0x00c
+ access_mode WO
+ bit CLRSELTIMEO 0x80
+ bit CLRATNO 0x40
+ bit CLRSCSIRSTI 0x20
+ bit CLRBUSFREE 0x08
+ bit CLRSCSIPERR 0x04
+ bit CLRPHASECHG 0x02
+ bit CLRREQINIT 0x01
+}
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+register SSTAT1 {
+ address 0x00c
+ access_mode RO
+ bit SELTO 0x80
+ bit ATNTARG 0x40
+ bit SCSIRSTI 0x20
+ bit PHASEMIS 0x10
+ bit BUSFREE 0x08
+ bit SCSIPERR 0x04
+ bit PHASECHG 0x02
+ bit REQINIT 0x01
+}
+
+/*
+ * SCSI Status 2 (pp. 3-25,26)
+ */
+register SSTAT2 {
+ address 0x00d
+ access_mode RO
+ bit OVERRUN 0x80
+ bit SHVALID 0x40 /* Shaddow Layer non-zero */
+ bit EXP_ACTIVE 0x10 /* SCSI Expander Active */
+ bit CRCVALERR 0x08 /* CRC doesn't match (U3 only) */
+ bit CRCENDERR 0x04 /* No terminal CRC packet (U3 only) */
+ bit CRCREQERR 0x02 /* Illegal CRC packet req (U3 only) */
+ bit DUAL_EDGE_ERR 0x01 /* Incorrect data phase (U3 only) */
+ mask SFCNT 0x1f
+}
+
+/*
+ * SCSI Status 3 (p. 3-26)
+ */
+register SSTAT3 {
+ address 0x00e
+ access_mode RO
+ mask SCSICNT 0xf0
+ mask OFFCNT 0x0f
+ mask U2OFFCNT 0x7f
+}
+
+/*
+ * SCSI ID for the aic7890/91 chips
+ */
+register SCSIID_ULTRA2 {
+ address 0x00f
+ access_mode RW
+ mask TID 0xf0 /* Target ID mask */
+ mask OID 0x0f /* Our ID mask */
+}
+
+/*
+ * SCSI Interrupt Mode 1 (p. 3-28)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+ address 0x010
+ access_mode RW
+ bit ENSELDO 0x40
+ bit ENSELDI 0x20
+ bit ENSELINGO 0x10
+ bit ENSWRAP 0x08
+ bit ENIOERR 0x08 /* LVD Tranceiver mode changes */
+ bit ENSDONE 0x04
+ bit ENSPIORDY 0x02
+ bit ENDMADONE 0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+ address 0x011
+ access_mode RW
+ bit ENSELTIMO 0x80
+ bit ENATNTARG 0x40
+ bit ENSCSIRST 0x20
+ bit ENPHASEMIS 0x10
+ bit ENBUSFREE 0x08
+ bit ENSCSIPERR 0x04
+ bit ENPHASECHG 0x02
+ bit ENREQINIT 0x01
+}
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+register SCSIBUSL {
+ address 0x012
+ access_mode RW
+}
+
+register SCSIBUSH {
+ address 0x013
+ access_mode RW
+}
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transferred on the SCSI bus. They are counted up in the same
+ * manner as STCNT is counted down. SHADDR should always be used
+ * to determine the address of the last byte transferred since HADDR
+ * can be skewed by write ahead.
+ */
+register SHADDR {
+ address 0x014
+ size 4
+ access_mode RO
+}
+
+/*
+ * Selection Timeout Timer (p. 3-30)
+ */
+register SELTIMER {
+ address 0x018
+ access_mode RW
+ bit STAGE6 0x20
+ bit STAGE5 0x10
+ bit STAGE4 0x08
+ bit STAGE3 0x04
+ bit STAGE2 0x02
+ bit STAGE1 0x01
+ alias TARGIDIN
+}
+
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id. The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+ address 0x019
+ access_mode RW
+ mask SELID_MASK 0xf0
+ bit ONEBIT 0x08
+}
+
+register SCAMCTL {
+ address 0x01a
+ access_mode RW
+ bit ENSCAMSELO 0x80
+ bit CLRSCAMSELID 0x40
+ bit ALTSTIM 0x20
+ bit DFLTTID 0x10
+ mask SCAMLVL 0x03
+}
+
+/*
+ * Target Mode Selecting in ID bitmask (aic7890/91/96/97)
+ */
+register TARGID {
+ address 0x01b
+ size 2
+ access_mode RW
+}
+
+/*
+ * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
+ * Indicates if external logic has been attached to the chip to
+ * perform the tasks of accessing a serial eeprom, testing termination
+ * strength, and performing cable detection. On the aic7860, most of
+ * these features are handled on chip, but on the aic7855 an attached
+ * aic3800 does the grunt work.
+ */
+register SPIOCAP {
+ address 0x01b
+ access_mode RW
+ bit SOFT1 0x80
+ bit SOFT0 0x40
+ bit SOFTCMDEN 0x20
+ bit EXT_BRDCTL 0x10 /* External Board control */
+ bit SEEPROM 0x08 /* External serial eeprom logic */
+ bit EEPROM 0x04 /* Writable external BIOS ROM */
+ bit ROM 0x02 /* Logic for accessing external ROM */
+ bit SSPIOCPS 0x01 /* Termination and cable detection */
+}
+
+register BRDCTL {
+ address 0x01d
+ bit BRDDAT7 0x80
+ bit BRDDAT6 0x40
+ bit BRDDAT5 0x20
+ bit BRDSTB 0x10
+ bit BRDCS 0x08
+ bit BRDRW 0x04
+ bit BRDCTL1 0x02
+ bit BRDCTL0 0x01
+ /* 7890 Definitions */
+ bit BRDDAT4 0x10
+ bit BRDDAT3 0x08
+ bit BRDDAT2 0x04
+ bit BRDRW_ULTRA2 0x02
+ bit BRDSTB_ULTRA2 0x01
+}
+
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device. In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device. When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM. When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.
+ *
+ * After successful arbitration for the memory port, the SEECS bit of
+ * the SEECTL register is connected to the chip select. The SEECK,
+ * SEEDO, and SEEDI are connected to the clock, data out, and data in
+ * lines respectively. The SEERDY bit of SEECTL is useful in that it
+ * gives us an 800 nsec timer. After a write to the SEECTL register,
+ * the SEERDY goes high 800 nsec later. The one exception to this is
+ * when we first request access to the memory port. The SEERDY goes
+ * high to signify that access has been granted and, for this case, has
+ * no implied timing.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to
+ * read the serial EEPROM.
+ */
+register SEECTL {
+ address 0x01e
+ bit EXTARBACK 0x80
+ bit EXTARBREQ 0x40
+ bit SEEMS 0x20
+ bit SEERDY 0x10
+ bit SEECS 0x08
+ bit SEECK 0x04
+ bit SEEDO 0x02
+ bit SEEDI 0x01
+}
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection. In a twin channel configuration
+ * addresses 0x00-0x1e are gated to the appropriate channel based on this
+ * register. SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+register SBLKCTL {
+ address 0x01f
+ access_mode RW
+ bit DIAGLEDEN 0x80 /* Aic78X0 only */
+ bit DIAGLEDON 0x40 /* Aic78X0 only */
+ bit AUTOFLUSHDIS 0x20
+ bit SELBUSB 0x08
+ bit ENAB40 0x08 /* LVD transceiver active */
+ bit ENAB20 0x04 /* SE/HVD transceiver active */
+ bit SELWIDE 0x02
+ bit XCVR 0x01 /* External transceiver active */
+}
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+register SEQCTL {
+ address 0x060
+ access_mode RW
+ bit PERRORDIS 0x80
+ bit PAUSEDIS 0x40
+ bit FAILDIS 0x20
+ bit FASTMODE 0x10
+ bit BRKADRINTEN 0x08
+ bit STEP 0x04
+ bit SEQRESET 0x02
+ bit LOADRAM 0x01
+}
+
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
+ * four bytes in succession. The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+register SEQRAM {
+ address 0x061
+ access_mode RW
+}
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+register SEQADDR0 {
+ address 0x062
+ access_mode RW
+}
+
+register SEQADDR1 {
+ address 0x063
+ access_mode RW
+ mask SEQADDR1_MASK 0x01
+}
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+register ACCUM {
+ address 0x064
+ access_mode RW
+ accumulator
+}
+
+register SINDEX {
+ address 0x065
+ access_mode RW
+ sindex
+}
+
+register DINDEX {
+ address 0x066
+ access_mode RW
+}
+
+register ALLONES {
+ address 0x069
+ access_mode RO
+ allones
+}
+
+register ALLZEROS {
+ address 0x06a
+ access_mode RO
+ allzeros
+}
+
+register NONE {
+ address 0x06a
+ access_mode WO
+ none
+}
+
+register FLAGS {
+ address 0x06b
+ access_mode RO
+ bit ZERO 0x02
+ bit CARRY 0x01
+}
+
+register SINDIR {
+ address 0x06c
+ access_mode RO
+}
+
+register DINDIR {
+ address 0x06d
+ access_mode WO
+}
+
+register FUNCTION1 {
+ address 0x06e
+ access_mode RW
+}
+
+register STACK {
+ address 0x06f
+ access_mode RO
+}
+
+/*
+ * Board Control (p. 3-43)
+ */
+register BCTL {
+ address 0x084
+ access_mode RW
+ bit ACE 0x08
+ bit ENABLE 0x01
+}
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+register DSCOMMAND0 {
+ address 0x084
+ access_mode RW
+ bit CACHETHEN 0x80 /* Cache Threshold enable */
+ bit DPARCKEN 0x40 /* Data Parity Check Enable */
+ bit MPARCKEN 0x20 /* Memory Parity Check Enable */
+ bit EXTREQLCK 0x10 /* External Request Lock */
+ /* aic7890/91/96/97 only */
+ bit INTSCBRAMSEL 0x08 /* Internal SCB RAM Select */
+ bit RAMPS 0x04 /* External SCB RAM Present */
+ bit USCBSIZE32 0x02 /* Use 32byte SCB Page Size */
+ bit CIOPARCKEN 0x01 /* Internal bus parity error enable */
+}
+
+register DSCOMMAND1 {
+ address 0x085
+ access_mode RW
+ mask DSLATT 0xfc /* PCI latency timer (non-ultra2) */
+ bit HADDLDSEL1 0x02 /* Host Address Load Select Bits */
+ bit HADDLDSEL0 0x01
+}
+
+/*
+ * Bus On/Off Time (p. 3-44) aic7770 only
+ */
+register BUSTIME {
+ address 0x085
+ access_mode RW
+ mask BOFF 0xf0
+ mask BON 0x0f
+}
+
+/*
+ * Bus Speed (p. 3-45) aic7770 only
+ */
+register BUSSPD {
+ address 0x086
+ access_mode RW
+ mask DFTHRSH 0xc0
+ mask STBOFF 0x38
+ mask STBON 0x07
+ mask DFTHRSH_100 0xc0
+ mask DFTHRSH_75 0x80
+}
+
+/* aic7850/55/60/70/80/95 only */
+register DSPCISTATUS {
+ address 0x086
+ mask DFTHRSH_100 0xc0
+}
+
+/* aic7890/91/96/97 only */
+register HS_MAILBOX {
+ address 0x086
+ mask HOST_MAILBOX 0xF0
+ mask SEQ_MAILBOX 0x0F
+ mask HOST_TQINPOS 0x80 /* Boundary at either 0 or 128 */
+}
+
+const HOST_MAILBOX_SHIFT 4
+const SEQ_MAILBOX_SHIFT 0
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overall host control of the device.
+ */
+register HCNTRL {
+ address 0x087
+ access_mode RW
+ bit POWRDN 0x40
+ bit SWINT 0x10
+ bit IRQMS 0x08
+ bit PAUSE 0x04
+ bit INTEN 0x02
+ bit CHIPRST 0x01
+ bit CHIPRSTACK 0x01
+}
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transferred across the host bus.
+ */
+register HADDR {
+ address 0x088
+ size 4
+ access_mode RW
+}
+
+register HCNT {
+ address 0x08c
+ size 3
+ access_mode RW
+}
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the SCBs into the SCBARRAY window.
+ */
+register SCBPTR {
+ address 0x090
+ access_mode RW
+}
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+register INTSTAT {
+ address 0x091
+ access_mode RW
+ bit BRKADRINT 0x08
+ bit SCSIINT 0x04
+ bit CMDCMPLT 0x02
+ bit SEQINT 0x01
+ mask BAD_PHASE SEQINT /* unknown scsi bus phase */
+ mask SEND_REJECT 0x10|SEQINT /* sending a message reject */
+ mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/
+ mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */
+ mask IGN_WIDE_RES 0x40|SEQINT /* Complex IGN Wide Res Msg */
+ mask PDATA_REINIT 0x50|SEQINT /*
+ * Returned to data phase
+ * that requires data
+ * transfer pointers to be
+ * recalculated from the
+ * transfer residual.
+ */
+ mask HOST_MSG_LOOP 0x60|SEQINT /*
+ * The bus is ready for the
+ * host to perform another
+ * message transaction. This
+ * mechanism is used for things
+ * like sync/wide negotiation
+ * that require a kernel based
+ * message state engine.
+ */
+ mask BAD_STATUS 0x70|SEQINT /* Bad status from target */
+ mask PERR_DETECTED 0x80|SEQINT /*
+ * Either the phase_lock
+ * or inb_next routine has
+ * noticed a parity error.
+ */
+ mask DATA_OVERRUN 0x90|SEQINT /*
+ * Target attempted to write
+ * beyond the bounds of its
+ * command.
+ */
+ mask MKMSG_FAILED 0xa0|SEQINT /*
+ * Target completed command
+ * without honoring our ATN
+ * request to issue a message.
+ */
+ mask MISSED_BUSFREE 0xb0|SEQINT /*
+ * The sequencer never saw
+ * the bus go free after
+ * either a command complete
+ * or disconnect message.
+ */
+ mask SCB_MISMATCH 0xc0|SEQINT /*
+ * Downloaded SCB's tag does
+ * not match the entry we
+ * intended to download.
+ */
+ mask NO_FREE_SCB 0xd0|SEQINT /*
+ * get_free_or_disc_scb failed.
+ */
+ mask OUT_OF_RANGE 0xe0|SEQINT
+
+ mask SEQINT_MASK 0xf0|SEQINT /* SEQINT Status Codes */
+ mask INT_PEND (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
+}
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors. You usually cannot recover from
+ * these without a full board reset.
+ */
+register ERROR {
+ address 0x092
+ access_mode RO
+ bit CIOPARERR 0x80 /* Ultra2 only */
+ bit PCIERRSTAT 0x40 /* PCI only */
+ bit MPARERR 0x20 /* PCI only */
+ bit DPARERR 0x10 /* PCI only */
+ bit SQPARERR 0x08
+ bit ILLOPCODE 0x04
+ bit ILLSADDR 0x02
+ bit ILLHADDR 0x01
+}
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+register CLRINT {
+ address 0x092
+ access_mode WO
+ bit CLRPARERR 0x10 /* PCI only */
+ bit CLRBRKADRINT 0x08
+ bit CLRSCSIINT 0x04
+ bit CLRCMDINT 0x02
+ bit CLRSEQINT 0x01
+}
+
+register DFCNTRL {
+ address 0x093
+ access_mode RW
+ bit PRELOADEN 0x80 /* aic7890 only */
+ bit WIDEODD 0x40
+ bit SCSIEN 0x20
+ bit SDMAEN 0x10
+ bit SDMAENACK 0x10
+ bit HDMAEN 0x08
+ bit HDMAENACK 0x08
+ bit DIRECTION 0x04
+ bit FIFOFLUSH 0x02
+ bit FIFORESET 0x01
+}
+
+register DFSTATUS {
+ address 0x094
+ access_mode RO
+ bit PRELOAD_AVAIL 0x80
+ bit DFCACHETH 0x40
+ bit FIFOQWDEMP 0x20
+ bit MREQPEND 0x10
+ bit HDONE 0x08
+ bit DFTHRESH 0x04
+ bit FIFOFULL 0x02
+ bit FIFOEMP 0x01
+}
+
+register DFWADDR {
+ address 0x95
+ access_mode RW
+}
+
+register DFRADDR {
+ address 0x97
+ access_mode RW
+}
+
+register DFDAT {
+ address 0x099
+ access_mode RW
+}
+
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+register SCBCNT {
+ address 0x09a
+ access_mode RW
+ bit SCBAUTO 0x80
+ mask SCBCNT_MASK 0x1f
+}
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+register QINFIFO {
+ address 0x09b
+ access_mode RW
+}
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+register QINCNT {
+ address 0x09c
+ access_mode RO
+}
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+register QOUTFIFO {
+ address 0x09d
+ access_mode WO
+}
+
+register CRCCONTROL1 {
+ address 0x09d
+ access_mode RW
+ bit CRCONSEEN 0x80
+ bit CRCVALCHKEN 0x40
+ bit CRCENDCHKEN 0x20
+ bit CRCREQCHKEN 0x10
+ bit TARGCRCENDEN 0x08
+ bit TARGCRCCNTEN 0x04
+}
+
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+register QOUTCNT {
+ address 0x09e
+ access_mode RO
+}
+
+register SCSIPHASE {
+ address 0x09e
+ access_mode RO
+ bit STATUS_PHASE 0x20
+ bit COMMAND_PHASE 0x10
+ bit MSG_IN_PHASE 0x08
+ bit MSG_OUT_PHASE 0x04
+ bit DATA_IN_PHASE 0x02
+ bit DATA_OUT_PHASE 0x01
+ mask DATA_PHASE_MASK 0x03
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+ address 0x09f
+ access_mode RW
+ bit ALT_MODE 0x80
+}
+
+/*
+ * SCB Definition (p. 5-4)
+ */
+scb {
+ address 0x0a0
+ size 64
+
+ SCB_CDB_PTR {
+ size 4
+ alias SCB_RESIDUAL_DATACNT
+ alias SCB_CDB_STORE
+ }
+ SCB_RESIDUAL_SGPTR {
+ size 4
+ }
+ SCB_SCSI_STATUS {
+ size 1
+ }
+ SCB_TARGET_PHASES {
+ size 1
+ }
+ SCB_TARGET_DATA_DIR {
+ size 1
+ }
+ SCB_TARGET_ITAG {
+ size 1
+ }
+ SCB_DATAPTR {
+ size 4
+ }
+ SCB_DATACNT {
+ /*
+ * The last byte is really the high address bits for
+ * the data address.
+ */
+ size 4
+ bit SG_LAST_SEG 0x80 /* In the fourth byte */
+ mask SG_HIGH_ADDR_BITS 0x7F /* In the fourth byte */
+ }
+ SCB_SGPTR {
+ size 4
+ bit SG_RESID_VALID 0x04 /* In the first byte */
+ bit SG_FULL_RESID 0x02 /* In the first byte */
+ bit SG_LIST_NULL 0x01 /* In the first byte */
+ }
+ SCB_CONTROL {
+ size 1
+ bit TARGET_SCB 0x80
+ bit DISCENB 0x40
+ bit TAG_ENB 0x20
+ bit MK_MESSAGE 0x10
+ bit ULTRAENB 0x08
+ bit DISCONNECTED 0x04
+ mask SCB_TAG_TYPE 0x03
+ }
+ SCB_SCSIID {
+ size 1
+ bit TWIN_CHNLB 0x80
+ mask TWIN_TID 0x70
+ mask TID 0xf0
+ mask OID 0x0f
+ }
+ SCB_LUN {
+ mask LID 0xff
+ size 1
+ }
+ SCB_TAG {
+ size 1
+ }
+ SCB_CDB_LEN {
+ size 1
+ }
+ SCB_SCSIRATE {
+ size 1
+ }
+ SCB_SCSIOFFSET {
+ size 1
+ }
+ SCB_NEXT {
+ size 1
+ }
+ SCB_64_SPARE {
+ size 16
+ }
+ SCB_64_BTT {
+ size 16
+ }
+}
+
+const SCB_UPLOAD_SIZE 32
+const SCB_DOWNLOAD_SIZE 32
+const SCB_DOWNLOAD_SIZE_64 48
+
+const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+register SEECTL_2840 {
+ address 0x0c0
+ access_mode RW
+ bit CS_2840 0x04
+ bit CK_2840 0x02
+ bit DO_2840 0x01
+}
+
+register STATUS_2840 {
+ address 0x0c1
+ access_mode RW
+ bit EEPROM_TF 0x80
+ mask BIOS_SEL 0x60
+ mask ADSEL 0x1e
+ bit DI_2840 0x01
+}
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+register CCHADDR {
+ address 0x0E0
+ size 8
+}
+
+register CCHCNT {
+ address 0x0E8
+}
+
+register CCSGRAM {
+ address 0x0E9
+}
+
+register CCSGADDR {
+ address 0x0EA
+}
+
+register CCSGCTL {
+ address 0x0EB
+ bit CCSGDONE 0x80
+ bit CCSGEN 0x08
+ bit SG_FETCH_NEEDED 0x02 /* Bit used for software state */
+ bit CCSGRESET 0x01
+}
+
+register CCSCBCNT {
+ address 0xEF
+}
+
+register CCSCBCTL {
+ address 0x0EE
+ bit CCSCBDONE 0x80
+ bit ARRDONE 0x40 /* SCB Array prefetch done */
+ bit CCARREN 0x10
+ bit CCSCBEN 0x08
+ bit CCSCBDIR 0x04
+ bit CCSCBRESET 0x01
+}
+
+register CCSCBADDR {
+ address 0x0ED
+}
+
+register CCSCBRAM {
+ address 0xEC
+}
+
+/*
+ * SCB bank address (7895/7896/97 only)
+ */
+register SCBBADDR {
+ address 0x0F0
+ access_mode RW
+}
+
+register CCSCBPTR {
+ address 0x0F1
+}
+
+register HNSCB_QOFF {
+ address 0x0F4
+}
+
+register SNSCB_QOFF {
+ address 0x0F6
+}
+
+register SDSCB_QOFF {
+ address 0x0F8
+}
+
+register QOFF_CTLSTA {
+ address 0x0FA
+ bit SCB_AVAIL 0x40
+ bit SNSCB_ROLLOVER 0x20
+ bit SDSCB_ROLLOVER 0x10
+ mask SCB_QSIZE 0x07
+ mask SCB_QSIZE_256 0x06
+}
+
+register DFF_THRSH {
+ address 0x0FB
+ mask WR_DFTHRSH 0x70
+ mask RD_DFTHRSH 0x07
+ mask RD_DFTHRSH_MIN 0x00
+ mask RD_DFTHRSH_25 0x01
+ mask RD_DFTHRSH_50 0x02
+ mask RD_DFTHRSH_63 0x03
+ mask RD_DFTHRSH_75 0x04
+ mask RD_DFTHRSH_85 0x05
+ mask RD_DFTHRSH_90 0x06
+ mask RD_DFTHRSH_MAX 0x07
+ mask WR_DFTHRSH_MIN 0x00
+ mask WR_DFTHRSH_25 0x10
+ mask WR_DFTHRSH_50 0x20
+ mask WR_DFTHRSH_63 0x30
+ mask WR_DFTHRSH_75 0x40
+ mask WR_DFTHRSH_85 0x50
+ mask WR_DFTHRSH_90 0x60
+ mask WR_DFTHRSH_MAX 0x70
+}
+
+register SG_CACHE_PRE {
+ access_mode WO
+ address 0x0fc
+ mask SG_ADDR_MASK 0xf8
+ bit ODD_SEG 0x04
+ bit LAST_SEG 0x02
+ bit LAST_SEG_DONE 0x01
+}
+
+register SG_CACHE_SHADOW {
+ access_mode RO
+ address 0x0fc
+ mask SG_ADDR_MASK 0xf8
+ bit ODD_SEG 0x04
+ bit LAST_SEG 0x02
+ bit LAST_SEG_DONE 0x01
+}
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, writing the synchronous negotiation results
+ * on top of the BIOS values, so we re-use those for our per-target
+ * scratchspace (actually a value that can be copied directly into
+ * SCSIRATE). The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space. This should work regardless of
+ * whether the bios has been installed.
+ */
+
+scratch_ram {
+ address 0x020
+ size 58
+
+ /*
+ * 1 byte per target starting at this address for configuration values
+ */
+ BUSY_TARGETS {
+ alias TARG_SCSIRATE
+ size 16
+ }
+ /*
+ * Bit vector of targets that have ULTRA enabled as set by
+ * the BIOS. The Sequencer relies on a per-SCB field to
+ * control whether to enable Ultra transfers or not. During
+ * initialization, we read this field and reuse it for 2
+ * entries in the busy target table.
+ */
+ ULTRA_ENB {
+ alias CMDSIZE_TABLE
+ size 2
+ }
+ /*
+ * Bit vector of targets that have disconnection disabled as set by
+ * the BIOS. The Sequencer relies in a per-SCB field to control the
+ * disconnect priveldge. During initialization, we read this field
+ * and reuse it for 2 entries in the busy target table.
+ */
+ DISC_DSB {
+ size 2
+ }
+ CMDSIZE_TABLE_TAIL {
+ size 4
+ }
+ /*
+ * Partial transfer past cacheline end to be
+ * transferred using an extra S/G.
+ */
+ MWI_RESIDUAL {
+ size 1
+ }
+ /*
+ * SCBID of the next SCB to be started by the controller.
+ */
+ NEXT_QUEUED_SCB {
+ size 1
+ }
+ /*
+ * Single byte buffer used to designate the type or message
+ * to send to a target.
+ */
+ MSG_OUT {
+ size 1
+ }
+ /* Parameters for DMA Logic */
+ DMAPARAMS {
+ size 1
+ bit PRELOADEN 0x80
+ bit WIDEODD 0x40
+ bit SCSIEN 0x20
+ bit SDMAEN 0x10
+ bit SDMAENACK 0x10
+ bit HDMAEN 0x08
+ bit HDMAENACK 0x08
+ bit DIRECTION 0x04 /* Set indicates PCI->SCSI */
+ bit FIFOFLUSH 0x02
+ bit FIFORESET 0x01
+ }
+ SEQ_FLAGS {
+ size 1
+ bit IDENTIFY_SEEN 0x80
+ bit TARGET_CMD_IS_TAGGED 0x40
+ bit DPHASE 0x20
+ /* Target flags */
+ bit TARG_CMD_PENDING 0x10
+ bit CMDPHASE_PENDING 0x08
+ bit DPHASE_PENDING 0x04
+ bit SPHASE_PENDING 0x02
+ bit NO_DISCONNECT 0x01
+ }
+ /*
+ * Temporary storage for the
+ * target/channel/lun of a
+ * reconnecting target
+ */
+ SAVED_SCSIID {
+ size 1
+ }
+ SAVED_LUN {
+ size 1
+ }
+ /*
+ * The last bus phase as seen by the sequencer.
+ */
+ LASTPHASE {
+ size 1
+ bit CDI 0x80
+ bit IOI 0x40
+ bit MSGI 0x20
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+ mask P_BUSFREE 0x01
+ }
+ /*
+ * head of list of SCBs awaiting
+ * selection
+ */
+ WAITING_SCBH {
+ size 1
+ }
+ /*
+ * head of list of SCBs that are
+ * disconnected. Used for SCB
+ * paging.
+ */
+ DISCONNECTED_SCBH {
+ size 1
+ }
+ /*
+ * head of list of SCBs that are
+ * not in use. Used for SCB paging.
+ */
+ FREE_SCBH {
+ size 1
+ }
+ /*
+ * head of list of SCBs that have
+ * completed but have not been
+ * put into the qoutfifo.
+ */
+ COMPLETE_SCBH {
+ size 1
+ }
+ /*
+ * Address of the hardware scb array in the host.
+ */
+ HSCB_ADDR {
+ size 4
+ }
+ /*
+ * Base address of our shared data with the kernel driver in host
+ * memory. This includes the qoutfifo and target mode
+ * incoming command queue.
+ */
+ SHARED_DATA_ADDR {
+ size 4
+ }
+ KERNEL_QINPOS {
+ size 1
+ }
+ QINPOS {
+ size 1
+ }
+ QOUTPOS {
+ size 1
+ }
+ /*
+ * Kernel and sequencer offsets into the queue of
+ * incoming target mode command descriptors. The
+ * queue is full when the KERNEL_TQINPOS == TQINPOS.
+ */
+ KERNEL_TQINPOS {
+ size 1
+ }
+ TQINPOS {
+ size 1
+ }
+ ARG_1 {
+ size 1
+ mask SEND_MSG 0x80
+ mask SEND_SENSE 0x40
+ mask SEND_REJ 0x20
+ mask MSGOUT_PHASEMIS 0x10
+ mask EXIT_MSG_LOOP 0x08
+ mask CONT_MSG_LOOP 0x04
+ mask CONT_TARG_SESSION 0x02
+ alias RETURN_1
+ }
+ ARG_2 {
+ size 1
+ alias RETURN_2
+ }
+
+ /*
+ * Snapshot of MSG_OUT taken after each message is sent.
+ */
+ LAST_MSG {
+ size 1
+ }
+
+ /*
+ * Sequences the kernel driver has okayed for us. This allows
+ * the driver to do things like prevent initiator or target
+ * operations.
+ */
+ SCSISEQ_TEMPLATE {
+ size 1
+ bit ENSELO 0x40
+ bit ENSELI 0x20
+ bit ENRSELI 0x10
+ bit ENAUTOATNO 0x08
+ bit ENAUTOATNI 0x04
+ bit ENAUTOATNP 0x02
+ }
+
+ /*
+ * Track whether the transfer byte count for
+ * the current data phase is odd.
+ */
+ DATA_COUNT_ODD {
+ size 1
+ }
+}
+
+scratch_ram {
+ address 0x056
+ size 4
+ /*
+ * These scratch ram locations are initialized by the 274X BIOS.
+ * We reuse them after capturing the BIOS settings during
+ * initialization.
+ */
+
+ /*
+ * The initiator specified tag for this target mode transaction.
+ */
+ HA_274_BIOSGLOBAL {
+ size 1
+ bit HA_274_EXTENDED_TRANS 0x01
+ alias INITIATOR_TAG
+ }
+
+ SEQ_FLAGS2 {
+ size 1
+ bit SCB_DMA 0x01
+ bit TARGET_MSG_PENDING 0x02
+ }
+}
+
+scratch_ram {
+ address 0x05a
+ size 6
+ /*
+ * These are reserved registers in the card's scratch ram on the 2742.
+ * The EISA configuraiton chip is mapped here. On Rev E. of the
+ * aic7770, the sequencer can use this area for scratch, but the
+ * host cannot directly access these registers. On later chips, this
+ * area can be read and written by both the host and the sequencer.
+ * Even on later chips, many of these locations are initialized by
+ * the BIOS.
+ */
+ SCSICONF {
+ size 1
+ bit TERM_ENB 0x80
+ bit RESET_SCSI 0x40
+ bit ENSPCHK 0x20
+ mask HSCSIID 0x07 /* our SCSI ID */
+ mask HWSCSIID 0x0f /* our SCSI ID if Wide Bus */
+ }
+ INTDEF {
+ address 0x05c
+ size 1
+ bit EDGE_TRIG 0x80
+ mask VECTOR 0x0f
+ }
+ HOSTCONF {
+ address 0x05d
+ size 1
+ }
+ HA_274_BIOSCTRL {
+ address 0x05f
+ size 1
+ mask BIOSMODE 0x30
+ mask BIOSDISABLED 0x30
+ bit CHANNEL_B_PRIMARY 0x08
+ }
+}
+
+scratch_ram {
+ address 0x070
+ size 16
+
+ /*
+ * Per target SCSI offset values for Ultra2 controllers.
+ */
+ TARG_OFFSET {
+ size 16
+ }
+}
+
+const TID_SHIFT 4
+const SCB_LIST_NULL 0xff
+const TARGET_CMD_CMPLT 0xfe
+
+const CCSGADDR_MAX 0x80
+const CCSGRAM_MAXSEGS 16
+
+/* WDTR Message values */
+const BUS_8_BIT 0x00
+const BUS_16_BIT 0x01
+const BUS_32_BIT 0x02
+
+/* Offset maximums */
+const MAX_OFFSET_8BIT 0x0f
+const MAX_OFFSET_16BIT 0x08
+const MAX_OFFSET_ULTRA2 0x7f
+const HOST_MSG 0xff
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT 0x05
+
+const STATUS_BUSY 0x08
+const STATUS_QUEUE_FULL 0x28
+const TARGET_DATA_IN 1
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+/* Offsets into the SCBID array where different data is stored */
+const QOUTFIFO_OFFSET download
+const QINFIFO_OFFSET download
+const CACHESIZE_MASK download
+const INVERTED_CACHESIZE_MASK download
+const SG_PREFETCH_CNT download
+const SG_PREFETCH_ALIGN_MASK download
+const SG_PREFETCH_ADDR_MASK download
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx.seq b/xen/drivers/scsi/aic7xxx/aic7xxx.seq
new file mode 100644
index 0000000000..db64c6fa56
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx.seq
@@ -0,0 +1,2365 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.94.2.16 2002/04/29 19:36:30 gibbs Exp $
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#43 $"
+PATCH_ARG_LIST = "struct ahc_softc *ahc"
+
+#include "aic7xxx.reg"
+#include "scsi_message.h"
+
+/*
+ * A few words on the waiting SCB list:
+ * After starting the selection hardware, we check for reconnecting targets
+ * as well as for our selection to complete just in case the reselection wins
+ * bus arbitration. The problem with this is that we must keep track of the
+ * SCB that we've already pulled from the QINFIFO and started the selection
+ * on just in case the reselection wins so that we can retry the selection at
+ * a later time. This problem cannot be resolved by holding a single entry
+ * in scratch ram since a reconnecting target can request sense and this will
+ * create yet another SCB waiting for selection. The solution used here is to
+ * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
+ * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes,
+ * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to
+ * this list everytime a request sense occurs or after completing a non-tagged
+ * command for which a second SCB has been queued. The sequencer will
+ * automatically consume the entries.
+ */
+
+bus_free_sel:
+ /*
+ * Turn off the selection hardware. We need to reset the
+ * selection request in order to perform a new selection.
+ */
+ and SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
+ and SIMODE1, ~ENBUSFREE;
+poll_for_work:
+ call clear_target_state;
+ and SXFRCTL0, ~SPIOEN;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ clr SCSIBUSL;
+ }
+ test SCSISEQ, ENSELO jnz poll_for_selection;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ xor SBLKCTL,SELBUSB; /* Toggle to the other bus */
+ test SCSISEQ, ENSELO jnz poll_for_selection;
+ }
+ cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+poll_for_work_loop:
+ if ((ahc->features & AHC_TWIN) != 0) {
+ xor SBLKCTL,SELBUSB; /* Toggle to the other bus */
+ }
+ test SSTAT0, SELDO|SELDI jnz selection;
+test_queue:
+ /* Has the driver posted any work for us? */
+BEGIN_CRITICAL;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+ } else {
+ mov A, QINPOS;
+ cmp KERNEL_QINPOS, A je poll_for_work_loop;
+ }
+ mov ARG_1, NEXT_QUEUED_SCB;
+
+ /*
+ * We have at least one queued SCB now and we don't have any
+ * SCBs in the list of SCBs awaiting selection. Allocate a
+ * card SCB for the host's SCB and get to work on it.
+ */
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mov ALLZEROS call get_free_or_disc_scb;
+ } else {
+ /* In the non-paging case, the SCBID == hardware SCB index */
+ mov SCBPTR, ARG_1;
+ }
+ or SEQ_FLAGS2, SCB_DMA;
+END_CRITICAL;
+dma_queued_scb:
+ /*
+ * DMA the SCB from host ram into the current SCB location.
+ */
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ mov ARG_1 call dma_scb;
+ /*
+ * Check one last time to see if this SCB was canceled
+ * before we completed the DMA operation. If it was,
+ * the QINFIFO next pointer will not match our saved
+ * value.
+ */
+ mov A, ARG_1;
+BEGIN_CRITICAL;
+ cmp NEXT_QUEUED_SCB, A jne abort_qinscb;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ cmp SCB_TAG, A je . + 2;
+ mvi SCB_MISMATCH call set_seqint;
+ }
+ mov NEXT_QUEUED_SCB, SCB_NEXT;
+ mov SCB_NEXT,WAITING_SCBH;
+ mov WAITING_SCBH, SCBPTR;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ mov NONE, SNSCB_QOFF;
+ } else {
+ inc QINPOS;
+ }
+ and SEQ_FLAGS2, ~SCB_DMA;
+END_CRITICAL;
+start_waiting:
+ /*
+ * Start the first entry on the waiting SCB list.
+ */
+ mov SCBPTR, WAITING_SCBH;
+ call start_selection;
+
+poll_for_selection:
+ /*
+ * Twin channel devices cannot handle things like SELTO
+ * interrupts on the "background" channel. So, while
+ * selecting, keep polling the current channel until
+ * either a selection or reselection occurs.
+ */
+ test SSTAT0, SELDO|SELDI jz poll_for_selection;
+
+selection:
+ /*
+ * We aren't expecting a bus free, so interrupt
+ * the kernel driver if it happens.
+ */
+ mvi CLRSINT1,CLRBUSFREE;
+ if ((ahc->features & AHC_DT) == 0) {
+ or SIMODE1, ENBUSFREE;
+ }
+
+ /*
+ * Guard against a bus free after (re)selection
+ * but prior to enabling the busfree interrupt. SELDI
+ * and SELDO will be cleared in that case.
+ */
+ test SSTAT0, SELDI|SELDO jz bus_free_sel;
+ test SSTAT0,SELDO jnz select_out;
+select_in:
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+ test SSTAT0, TARGET jz initiator_reselect;
+ }
+ mvi CLRSINT0, CLRSELDI;
+
+ /*
+ * We've just been selected. Assert BSY and
+ * setup the phase for receiving messages
+ * from the target.
+ */
+ mvi SCSISIGO, P_MESGOUT|BSYO;
+
+ /*
+ * Setup the DMA for sending the identify and
+ * command information.
+ */
+ or SEQ_FLAGS, CMDPHASE_PENDING;
+
+ mov A, TQINPOS;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi SHARED_DATA_ADDR call set_32byte_addr;
+ mvi CCSCBCTL, CCSCBRESET;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SHARED_DATA_ADDR call set_32byte_addr;
+ mvi DFCNTRL, FIFORESET;
+ }
+
+ /* Initiator that selected us */
+ and SAVED_SCSIID, SELID_MASK, SELID;
+ /* The Target ID we were selected at */
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ and A, OID, TARGIDIN;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ and A, OID, SCSIID_ULTRA2;
+ } else {
+ and A, OID, SCSIID;
+ }
+ or SAVED_SCSIID, A;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ test SBLKCTL, SELBUSB jz . + 2;
+ or SAVED_SCSIID, TWIN_CHNLB;
+ }
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, SAVED_SCSIID;
+ } else {
+ mov DFDAT, SAVED_SCSIID;
+ }
+
+ /*
+ * If ATN isn't asserted, the target isn't interested
+ * in talking to us. Go directly to bus free.
+ * XXX SCSI-1 may require us to assume lun 0 if
+ * ATN is false.
+ */
+ test SCSISIGI, ATNI jz target_busfree;
+
+ /*
+ * Watch ATN closely now as we pull in messages from the
+ * initiator. We follow the guidlines from section 6.5
+ * of the SCSI-2 spec for what messages are allowed when.
+ */
+ call target_inb;
+
+ /*
+ * Our first message must be one of IDENTIFY, ABORT, or
+ * BUS_DEVICE_RESET.
+ */
+ test DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop;
+ /* Store for host */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, DINDEX;
+ } else {
+ mov DFDAT, DINDEX;
+ }
+ and SAVED_LUN, MSG_IDENTIFY_LUNMASK, DINDEX;
+
+ /* Remember for disconnection decision */
+ test DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
+ /* XXX Honor per target settings too */
+ or SEQ_FLAGS, NO_DISCONNECT;
+
+ test SCSISIGI, ATNI jz ident_messages_done;
+ call target_inb;
+ /*
+ * If this is a tagged request, the tagged message must
+ * immediately follow the identify. We test for a valid
+ * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
+ * < MSG_IGN_WIDE_RESIDUE.
+ */
+ add A, -MSG_SIMPLE_Q_TAG, DINDEX;
+ jnc ident_messages_done_msg_pending;
+ add A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
+ jc ident_messages_done_msg_pending;
+
+ /* Store for host */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, DINDEX;
+ } else {
+ mov DFDAT, DINDEX;
+ }
+
+ /*
+ * If the initiator doesn't feel like providing a tag number,
+ * we've got a failed selection and must transition to bus
+ * free.
+ */
+ test SCSISIGI, ATNI jz target_busfree;
+
+ /*
+ * Store the tag for the host.
+ */
+ call target_inb;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, DINDEX;
+ } else {
+ mov DFDAT, DINDEX;
+ }
+ mov INITIATOR_TAG, DINDEX;
+ or SEQ_FLAGS, TARGET_CMD_IS_TAGGED;
+
+ident_messages_done:
+ /* Terminate the ident list */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi CCSCBRAM, SCB_LIST_NULL;
+ } else {
+ mvi DFDAT, SCB_LIST_NULL;
+ }
+ or SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
+ test SEQ_FLAGS2, TARGET_MSG_PENDING
+ jnz target_mesgout_pending;
+ test SCSISIGI, ATNI jnz target_mesgout_continue;
+ jmp target_ITloop;
+
+
+ident_messages_done_msg_pending:
+ or SEQ_FLAGS2, TARGET_MSG_PENDING;
+ jmp ident_messages_done;
+
+ /*
+ * Pushed message loop to allow the kernel to
+ * run it's own target mode message state engine.
+ */
+host_target_message_loop:
+ mvi HOST_MSG_LOOP call set_seqint;
+ cmp RETURN_1, EXIT_MSG_LOOP je target_ITloop;
+ test SSTAT0, SPIORDY jz .;
+ jmp host_target_message_loop;
+ }
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Reselection has been initiated by a target. Make a note that we've been
+ * reselected, but haven't seen an IDENTIFY message from the target yet.
+ */
+initiator_reselect:
+ /* XXX test for and handle ONE BIT condition */
+ or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
+ and SAVED_SCSIID, SELID_MASK, SELID;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ and A, OID, SCSIID_ULTRA2;
+ } else {
+ and A, OID, SCSIID;
+ }
+ or SAVED_SCSIID, A;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ test SBLKCTL, SELBUSB jz . + 2;
+ or SAVED_SCSIID, TWIN_CHNLB;
+ }
+ mvi CLRSINT0, CLRSELDI;
+ jmp ITloop;
+}
+
+abort_qinscb:
+ call add_scb_to_free_list;
+ jmp poll_for_work_loop;
+
+start_selection:
+ /*
+ * If bus reset interrupts have been disabled (from a previous
+ * reset), re-enable them now. Resets are only of interest
+ * when we have outstanding transactions, so we can safely
+ * defer re-enabling the interrupt until, as an initiator,
+ * we start sending out transactions again.
+ */
+ test SIMODE1, ENSCSIRST jnz . + 3;
+ mvi CLRSINT1, CLRSCSIRSTI;
+ or SIMODE1, ENSCSIRST;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+ test SCB_SCSIID, TWIN_CHNLB jz . + 2;
+ or SINDEX, SELBUSB;
+ mov SBLKCTL,SINDEX; /* select channel */
+ }
+initialize_scsiid:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mov SCSIID_ULTRA2, SCB_SCSIID;
+ } else if ((ahc->features & AHC_TWIN) != 0) {
+ and SCSIID, TWIN_TID|OID, SCB_SCSIID;
+ } else {
+ mov SCSIID, SCB_SCSIID;
+ }
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ mov SINDEX, SCSISEQ_TEMPLATE;
+ test SCB_CONTROL, TARGET_SCB jz . + 2;
+ or SINDEX, TEMODE;
+ mov SCSISEQ, SINDEX ret;
+ } else {
+ mov SCSISEQ, SCSISEQ_TEMPLATE ret;
+ }
+
+/*
+ * Initialize transfer settings and clear the SCSI channel.
+ * SINDEX should contain any additional bit's the client wants
+ * set in SXFRCTL0. We also assume that the current SCB is
+ * a valid SCB for the target we wish to talk to.
+ */
+initialize_channel:
+ or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
+set_transfer_settings:
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ test SCB_CONTROL, ULTRAENB jz . + 2;
+ or SXFRCTL0, FAST20;
+ }
+ /*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov SCSIRATE, SCB_SCSIRATE, 2 ret;
+ } else {
+ mov SCSIRATE, SCB_SCSIRATE ret;
+ }
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * We carefully toggle SPIOEN to allow us to return the
+ * message byte we receive so it can be checked prior to
+ * driving REQ on the bus for the next byte.
+ */
+target_inb:
+ /*
+ * Drive REQ on the bus by enabling SCSI PIO.
+ */
+ or SXFRCTL0, SPIOEN;
+ /* Wait for the byte */
+ test SSTAT0, SPIORDY jz .;
+ /* Prevent our read from triggering another REQ */
+ and SXFRCTL0, ~SPIOEN;
+ /* Save latched contents */
+ mov DINDEX, SCSIDATL ret;
+}
+
+/*
+ * After the selection, remove this SCB from the "waiting SCB"
+ * list. This is achieved by simply moving our "next" pointer into
+ * WAITING_SCBH. Our next pointer will be set to null the next time this
+ * SCB is used, so don't bother with it now.
+ */
+select_out:
+ /* Turn off the selection hardware */
+ and SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
+ mov SCBPTR, WAITING_SCBH;
+ mov WAITING_SCBH,SCB_NEXT;
+ mov SAVED_SCSIID, SCB_SCSIID;
+ mov SAVED_LUN, SCB_LUN;
+ call initialize_channel;
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jz initiator_select;
+
+ /*
+ * We've just re-selected an initiator.
+ * Assert BSY and setup the phase for
+ * sending our identify messages.
+ */
+ mvi P_MESGIN|BSYO call change_phase;
+ mvi CLRSINT0, CLRSELDO;
+
+ /*
+ * Start out with a simple identify message.
+ */
+ or SCB_LUN, MSG_IDENTIFYFLAG call target_outb;
+
+ /*
+ * If we are the result of a tagged command, send
+ * a simple Q tag and the tag id.
+ */
+ test SCB_CONTROL, TAG_ENB jz . + 3;
+ mvi MSG_SIMPLE_Q_TAG call target_outb;
+ mov SCB_TARGET_ITAG call target_outb;
+target_synccmd:
+ /*
+ * Now determine what phases the host wants us
+ * to go through.
+ */
+ mov SEQ_FLAGS, SCB_TARGET_PHASES;
+
+ test SCB_CONTROL, MK_MESSAGE jz target_ITloop;
+ mvi P_MESGIN|BSYO call change_phase;
+ jmp host_target_message_loop;
+target_ITloop:
+ /*
+ * Start honoring ATN signals now that
+ * we properly identified ourselves.
+ */
+ test SCSISIGI, ATNI jnz target_mesgout;
+ test SEQ_FLAGS, CMDPHASE_PENDING jnz target_cmdphase;
+ test SEQ_FLAGS, DPHASE_PENDING jnz target_dphase;
+ test SEQ_FLAGS, SPHASE_PENDING jnz target_sphase;
+
+ /*
+ * No more work to do. Either disconnect or not depending
+ * on the state of NO_DISCONNECT.
+ */
+ test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect;
+ mov RETURN_1, ALLZEROS;
+ call complete_target_cmd;
+ cmp RETURN_1, CONT_MSG_LOOP jne .;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mov ALLZEROS call get_free_or_disc_scb;
+ }
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ mov SCB_TAG call dma_scb;
+ call set_transfer_settings;
+ jmp target_synccmd;
+
+target_mesgout:
+ mvi SCSISIGO, P_MESGOUT|BSYO;
+target_mesgout_continue:
+ call target_inb;
+target_mesgout_pending:
+ /* Local Processing goes here... */
+ jmp host_target_message_loop;
+
+target_disconnect:
+ mvi P_MESGIN|BSYO call change_phase;
+ test SEQ_FLAGS, DPHASE jz . + 2;
+ mvi MSG_SAVEDATAPOINTER call target_outb;
+ mvi MSG_DISCONNECT call target_outb;
+
+target_busfree_wait:
+ /* Wait for preceding I/O session to complete. */
+ test SCSISIGI, ACKI jnz .;
+target_busfree:
+ and SIMODE1, ~ENBUSFREE;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ clr SCSIBUSL;
+ }
+ clr SCSISIGO;
+ mvi LASTPHASE, P_BUSFREE;
+ call complete_target_cmd;
+ jmp poll_for_work;
+
+target_cmdphase:
+ /*
+ * The target has dropped ATN (doesn't want to abort or BDR)
+ * and we believe this selection to be valid. If the ring
+ * buffer for new commands is full, return busy or queue full.
+ */
+ if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+ and A, HOST_TQINPOS, HS_MAILBOX;
+ } else {
+ mov A, KERNEL_TQINPOS;
+ }
+ cmp TQINPOS, A jne tqinfifo_has_space;
+ mvi P_STATUS|BSYO call change_phase;
+ test SEQ_FLAGS, TARGET_CMD_IS_TAGGED jz . + 3;
+ mvi STATUS_QUEUE_FULL call target_outb;
+ jmp target_busfree_wait;
+ mvi STATUS_BUSY call target_outb;
+ jmp target_busfree_wait;
+tqinfifo_has_space:
+ mvi P_COMMAND|BSYO call change_phase;
+ call target_inb;
+ mov A, DINDEX;
+ /* Store for host */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, A;
+ } else {
+ mov DFDAT, A;
+ }
+
+ /*
+ * Determine the number of bytes to read
+ * based on the command group code via table lookup.
+ * We reuse the first 8 bytes of the TARG_SCSIRATE
+ * BIOS array for this table. Count is one less than
+ * the total for the command since we've already fetched
+ * the first byte.
+ */
+ shr A, CMD_GROUP_CODE_SHIFT;
+ add SINDEX, CMDSIZE_TABLE, A;
+ mov A, SINDIR;
+
+ test A, 0xFF jz command_phase_done;
+ or SXFRCTL0, SPIOEN;
+command_loop:
+ test SSTAT0, SPIORDY jz .;
+ cmp A, 1 jne . + 2;
+ and SXFRCTL0, ~SPIOEN; /* Last Byte */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, SCSIDATL;
+ } else {
+ mov DFDAT, SCSIDATL;
+ }
+ dec A;
+ test A, 0xFF jnz command_loop;
+
+command_phase_done:
+ and SEQ_FLAGS, ~CMDPHASE_PENDING;
+ jmp target_ITloop;
+
+target_dphase:
+ /*
+ * Data phases on the bus are from the
+ * perspective of the initiator. The dma
+ * code looks at LASTPHASE to determine the
+ * data direction of the DMA. Toggle it for
+ * target transfers.
+ */
+ xor LASTPHASE, IOI, SCB_TARGET_DATA_DIR;
+ or SCB_TARGET_DATA_DIR, BSYO call change_phase;
+ jmp p_data;
+
+target_sphase:
+ mvi P_STATUS|BSYO call change_phase;
+ mvi LASTPHASE, P_STATUS;
+ mov SCB_SCSI_STATUS call target_outb;
+ /* XXX Watch for ATN or parity errors??? */
+ mvi SCSISIGO, P_MESGIN|BSYO;
+ /* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
+ mov ALLZEROS call target_outb;
+ jmp target_busfree_wait;
+
+complete_target_cmd:
+ test SEQ_FLAGS, TARG_CMD_PENDING jnz . + 2;
+ mov SCB_TAG jmp complete_post;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Set the valid byte */
+ mvi CCSCBADDR, 24;
+ mov CCSCBRAM, ALLONES;
+ mvi CCHCNT, 28;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ clr CCSCBCTL;
+ } else {
+ /* Set the valid byte */
+ or DFCNTRL, FIFORESET;
+ mvi DFWADDR, 3; /* Third 64bit word or byte 24 */
+ mov DFDAT, ALLONES;
+ mvi 28 call set_hcnt;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ call dma_finish;
+ }
+ inc TQINPOS;
+ mvi INTSTAT,CMDCMPLT ret;
+ }
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+initiator_select:
+ /*
+ * As soon as we get a successful selection, the target
+ * should go into the message out phase since we have ATN
+ * asserted.
+ */
+ mvi MSG_OUT, MSG_IDENTIFYFLAG;
+ or SEQ_FLAGS, IDENTIFY_SEEN;
+ mvi CLRSINT0, CLRSELDO;
+
+ /*
+ * Main loop for information transfer phases. Wait for the
+ * target to assert REQ before checking MSG, C/D and I/O for
+ * the bus phase.
+ */
+mesgin_phasemis:
+ITloop:
+ call phase_lock;
+
+ mov A, LASTPHASE;
+
+ test A, ~P_DATAIN jz p_data;
+ cmp A,P_COMMAND je p_command;
+ cmp A,P_MESGOUT je p_mesgout;
+ cmp A,P_STATUS je p_status;
+ cmp A,P_MESGIN je p_mesgin;
+
+ mvi BAD_PHASE call set_seqint;
+ jmp ITloop; /* Try reading the bus again. */
+
+await_busfree:
+ and SIMODE1, ~ENBUSFREE;
+ mov NONE, SCSIDATL; /* Ack the last byte */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ clr SCSIBUSL; /* Prevent bit leakage durint SELTO */
+ }
+ and SXFRCTL0, ~SPIOEN;
+ test SSTAT1,REQINIT|BUSFREE jz .;
+ test SSTAT1, BUSFREE jnz poll_for_work;
+ mvi MISSED_BUSFREE call set_seqint;
+}
+
+clear_target_state:
+ /*
+ * We assume that the kernel driver may reset us
+ * at any time, even in the middle of a DMA, so
+ * clear DFCNTRL too.
+ */
+ clr DFCNTRL;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+
+ /*
+ * We don't know the target we will connect to,
+ * so default to narrow transfers to avoid
+ * parity problems.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov SCSIRATE, ALLZEROS, 2;
+ } else {
+ clr SCSIRATE;
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ and SXFRCTL0, ~(FAST20);
+ }
+ }
+ mvi LASTPHASE, P_BUSFREE;
+ /* clear target specific flags */
+ clr SEQ_FLAGS ret;
+
+sg_advance:
+ clr A; /* add sizeof(struct scatter) */
+ add SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
+ adc SCB_RESIDUAL_SGPTR[1],A;
+ adc SCB_RESIDUAL_SGPTR[2],A;
+ adc SCB_RESIDUAL_SGPTR[3],A ret;
+
+if ((ahc->features & AHC_CMD_CHAN) != 0) {
+disable_ccsgen:
+ test CCSGCTL, CCSGEN jz return;
+ test CCSGCTL, CCSGDONE jz .;
+disable_ccsgen_fetch_done:
+ clr CCSGCTL;
+ test CCSGCTL, CCSGEN jnz .;
+ ret;
+idle_loop:
+ /*
+ * Do we need any more segments for this transfer?
+ */
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz return;
+
+ /* Did we just finish fetching segs? */
+ cmp CCSGCTL, CCSGEN|CCSGDONE je idle_sgfetch_complete;
+
+ /* Are we actively fetching segments? */
+ test CCSGCTL, CCSGEN jnz return;
+
+ /*
+ * Do we have any prefetch left???
+ */
+ cmp CCSGADDR, SG_PREFETCH_CNT jne idle_sg_avail;
+
+ /*
+ * Need to fetch segments, but we can only do that
+ * if the command channel is completely idle. Make
+ * sure we don't have an SCB prefetch going on.
+ */
+ test CCSCBCTL, CCSCBEN jnz return;
+
+ /*
+ * We fetch a "cacheline aligned" and sized amount of data
+ * so we don't end up referencing a non-existant page.
+ * Cacheline aligned is in quotes because the kernel will
+ * set the prefetch amount to a reasonable level if the
+ * cacheline size is unknown.
+ */
+ mvi CCHCNT, SG_PREFETCH_CNT;
+ and CCHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+ bmov CCHADDR[1], SCB_RESIDUAL_SGPTR[1], 3;
+ mvi CCSGCTL, CCSGEN|CCSGRESET ret;
+idle_sgfetch_complete:
+ call disable_ccsgen_fetch_done;
+ and CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
+idle_sg_avail:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ /* Does the hardware have space for another SG entry? */
+ test DFSTATUS, PRELOAD_AVAIL jz return;
+ bmov HADDR, CCSGRAM, 7;
+ test HCNT[0], 0x1 jz . + 2;
+ xor DATA_COUNT_ODD, 0x1;
+ bmov SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ mov SCB_RESIDUAL_DATACNT[3] call set_hhaddr;
+ }
+ call sg_advance;
+ mov SINDEX, SCB_RESIDUAL_SGPTR[0];
+ test DATA_COUNT_ODD, 0x1 jz . + 2;
+ or SINDEX, ODD_SEG;
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+ or SINDEX, LAST_SEG;
+ mov SG_CACHE_PRE, SINDEX;
+ /* Load the segment */
+ or DFCNTRL, PRELOADEN;
+ }
+ ret;
+}
+
+if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+/*
+ * Calculate the trailing portion of this S/G segment that cannot
+ * be transferred using memory write and invalidate PCI transactions.
+ * XXX Can we optimize this for PCI writes only???
+ */
+calc_mwi_residual:
+ /*
+ * If the ending address is on a cacheline boundary,
+ * there is no need for an extra segment.
+ */
+ mov A, HCNT[0];
+ add A, A, HADDR[0];
+ and A, CACHESIZE_MASK;
+ test A, 0xFF jz return;
+
+ /*
+ * If the transfer is less than a cachline,
+ * there is no need for an extra segment.
+ */
+ test HCNT[1], 0xFF jnz calc_mwi_residual_final;
+ test HCNT[2], 0xFF jnz calc_mwi_residual_final;
+ add NONE, INVERTED_CACHESIZE_MASK, HCNT[0];
+ jnc return;
+
+calc_mwi_residual_final:
+ mov MWI_RESIDUAL, A;
+ not A;
+ inc A;
+ add HCNT[0], A;
+ adc HCNT[1], -1;
+ adc HCNT[2], -1 ret;
+}
+
+p_data:
+ test SEQ_FLAGS,IDENTIFY_SEEN jnz p_data_okay;
+ mvi NO_IDENT jmp set_seqint;
+p_data_okay:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+ } else {
+ mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+ }
+ test LASTPHASE, IOI jnz . + 2;
+ or DMAPARAMS, DIRECTION;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* We don't have any valid S/G elements */
+ mvi CCSGADDR, SG_PREFETCH_CNT;
+ }
+ test SEQ_FLAGS, DPHASE jz data_phase_initialize;
+
+ /*
+ * If we re-enter the data phase after going through another
+ * phase, our transfer location has almost certainly been
+ * corrupted by the interveining, non-data, transfers. Ask
+ * the host driver to fix us up based on the transfer residual.
+ */
+ mvi PDATA_REINIT call set_seqint;
+ jmp data_phase_loop;
+
+data_phase_initialize:
+ /* We have seen a data phase for the first time */
+ or SEQ_FLAGS, DPHASE;
+
+ /*
+ * Initialize the DMA address and counter from the SCB.
+ * Also set SCB_RESIDUAL_SGPTR, including the LAST_SEG
+ * flag in the highest byte of the data count. We cannot
+ * modify the saved values in the SCB until we see a save
+ * data pointers message.
+ */
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ /* The lowest address byte must be loaded last. */
+ mov SCB_DATACNT[3] call set_hhaddr;
+ }
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov HADDR, SCB_DATAPTR, 7;
+ bmov SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCB_DATAPTR call bcopy_7;
+ mvi DINDEX, SCB_RESIDUAL_DATACNT + 3;
+ mvi SCB_DATACNT + 3 call bcopy_5;
+ }
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+ call calc_mwi_residual;
+ }
+ and SCB_RESIDUAL_SGPTR[0], ~SG_FULL_RESID;
+ and DATA_COUNT_ODD, 0x1, HCNT[0];
+
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT, HCNT, 3;
+ } else {
+ call set_stcnt_from_hcnt;
+ }
+ }
+
+data_phase_loop:
+ /* Guard against overruns */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_inbounds;
+
+ /*
+ * Turn on `Bit Bucket' mode, wait until the target takes
+ * us to another phase, and then notify the host.
+ */
+ and DMAPARAMS, DIRECTION;
+ mov DFCNTRL, DMAPARAMS;
+ or SXFRCTL1,BITBUCKET;
+ if ((ahc->features & AHC_DT) == 0) {
+ test SSTAT1,PHASEMIS jz .;
+ } else {
+ test SCSIPHASE, DATA_PHASE_MASK jnz .;
+ }
+ and SXFRCTL1, ~BITBUCKET;
+ mvi DATA_OVERRUN call set_seqint;
+ jmp ITloop;
+
+data_phase_inbounds:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mov SINDEX, SCB_RESIDUAL_SGPTR[0];
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+ or SINDEX, LAST_SEG;
+ test DATA_COUNT_ODD, 0x1 jz . + 2;
+ or SINDEX, ODD_SEG;
+ mov SG_CACHE_PRE, SINDEX;
+ mov DFCNTRL, DMAPARAMS;
+ultra2_dma_loop:
+ call idle_loop;
+ /*
+ * The transfer is complete if either the last segment
+ * completes or the target changes phase.
+ */
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jnz ultra2_dmafinish;
+ if ((ahc->features & AHC_DT) == 0) {
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ /*
+ * As a target, we control the phases,
+ * so ignore PHASEMIS.
+ */
+ test SSTAT0, TARGET jnz ultra2_dma_loop;
+ }
+ if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+ test SSTAT1,PHASEMIS jz ultra2_dma_loop;
+ }
+ } else {
+ test DFCNTRL, SCSIEN jnz ultra2_dma_loop;
+ }
+
+ultra2_dmafinish:
+ /*
+ * The transfer has terminated either due to a phase
+ * change, and/or the completion of the last segment.
+ * We have two goals here. Do as much other work
+ * as possible while the data fifo drains on a read
+ * and respond as quickly as possible to the standard
+ * messages (save data pointers/disconnect and command
+ * complete) that usually follow a data phase.
+ */
+ if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
+ /*
+ * On chips with broken auto-flush, start
+ * the flushing process now. We'll poke
+ * the chip from time to time to keep the
+ * flush process going as we complete the
+ * data phase.
+ */
+ or DFCNTRL, FIFOFLUSH;
+ }
+ /*
+ * We assume that, even though data may still be
+ * transferring to the host, that the SCSI side of
+ * the DMA engine is now in a static state. This
+ * allows us to update our notion of where we are
+ * in this transfer.
+ *
+ * If, by chance, we stopped before being able
+ * to fetch additional segments for this transfer,
+ * yet the last S/G was completely exhausted,
+ * call our idle loop until it is able to load
+ * another segment. This will allow us to immediately
+ * pickup on the next segment on the next data phase.
+ *
+ * If we happened to stop on the last segment, then
+ * our residual information is still correct from
+ * the idle loop and there is no need to perform
+ * any fixups.
+ */
+ultra2_ensure_sg:
+ test SG_CACHE_SHADOW, LAST_SEG jz ultra2_shvalid;
+ /* Record if we've consumed all S/G entries */
+ test SSTAT2, SHVALID jnz residuals_correct;
+ or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+ jmp residuals_correct;
+
+ultra2_shvalid:
+ test SSTAT2, SHVALID jnz sgptr_fixup;
+ call idle_loop;
+ jmp ultra2_ensure_sg;
+
+sgptr_fixup:
+ /*
+ * Fixup the residual next S/G pointer. The S/G preload
+ * feature of the chip allows us to load two elements
+ * in addition to the currently active element. We
+ * store the bottom byte of the next S/G pointer in
+ * the SG_CACEPTR register so we can restore the
+ * correct value when the DMA completes. If the next
+ * sg ptr value has advanced to the point where higher
+ * bytes in the address have been affected, fix them
+ * too.
+ */
+ test SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
+ test SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
+ add SCB_RESIDUAL_SGPTR[1], -1;
+ adc SCB_RESIDUAL_SGPTR[2], -1;
+ adc SCB_RESIDUAL_SGPTR[3], -1;
+sgptr_fixup_done:
+ and SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
+ clr DATA_COUNT_ODD;
+ test SG_CACHE_SHADOW, ODD_SEG jz . + 2;
+ or DATA_COUNT_ODD, 0x1;
+ clr SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */
+residuals_correct:
+ /*
+ * Go ahead and shut down the DMA engine now.
+ * In the future, we'll want to handle end of
+ * transfer messages prior to doing this, but this
+ * requires similar restructuring for pre-ULTRA2
+ * controllers.
+ */
+ test DMAPARAMS, DIRECTION jnz ultra2_fifoempty;
+ultra2_fifoflush:
+ if ((ahc->features & AHC_DT) == 0) {
+ if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
+ /*
+ * On Rev A of the aic7890, the autoflush
+ * feature doesn't function correctly.
+ * Perform an explicit manual flush. During
+ * a manual flush, the FIFOEMP bit becomes
+ * true every time the PCI FIFO empties
+ * regardless of the state of the SCSI FIFO.
+ * It can take up to 4 clock cycles for the
+ * SCSI FIFO to get data into the PCI FIFO
+ * and for FIFOEMP to de-assert. Here we
+ * guard against this condition by making
+ * sure the FIFOEMP bit stays on for 5 full
+ * clock cycles.
+ */
+ or DFCNTRL, FIFOFLUSH;
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ }
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ } else {
+ /*
+ * We enable the auto-ack feature on DT capable
+ * controllers. This means that the controller may
+ * have already transferred some overrun bytes into
+ * the data FIFO and acked them on the bus. The only
+ * way to detect this situation is to wait for
+ * LAST_SEG_DONE to come true on a completed transfer
+ * and then test to see if the data FIFO is non-empty.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 4;
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jz .;
+ test DFSTATUS, FIFOEMP jnz ultra2_fifoempty;
+ /* Overrun */
+ jmp data_phase_loop;
+ test DFSTATUS, FIFOEMP jz .;
+ }
+ultra2_fifoempty:
+ /* Don't clobber an inprogress host data transfer */
+ test DFSTATUS, MREQPEND jnz ultra2_fifoempty;
+ultra2_dmahalt:
+ and DFCNTRL, ~(SCSIEN|HDMAEN);
+ test DFCNTRL, SCSIEN|HDMAEN jnz .;
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ /*
+ * Keep HHADDR cleared for future, 32bit addressed
+ * only, DMA operations.
+ *
+ * Due to bayonette style S/G handling, our residual
+ * data must be "fixed up" once the transfer is halted.
+ * Here we fixup the HSHADDR stored in the high byte
+ * of the residual data cnt. By postponing the fixup,
+ * we can batch the clearing of HADDR with the fixup.
+ * If we halted on the last segment, the residual is
+ * already correct. If we are not on the last
+ * segment, copy the high address directly from HSHADDR.
+ * We don't need to worry about maintaining the
+ * SG_LAST_SEG flag as it will always be false in the
+ * case where an update is required.
+ */
+ or DSCOMMAND1, HADDLDSEL0;
+ test SG_CACHE_SHADOW, LAST_SEG jnz . + 2;
+ mov SCB_RESIDUAL_DATACNT[3], SHADDR;
+ clr HADDR;
+ and DSCOMMAND1, ~HADDLDSEL0;
+ }
+ } else {
+ /* If we are the last SG block, tell the hardware. */
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+ && ahc->pci_cachesize != 0) {
+ test MWI_RESIDUAL, 0xFF jnz dma_mid_sg;
+ }
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz dma_mid_sg;
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jz dma_last_sg;
+ if ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0) {
+ test DMAPARAMS, DIRECTION jz dma_mid_sg;
+ }
+ }
+dma_last_sg:
+ and DMAPARAMS, ~WIDEODD;
+dma_mid_sg:
+ /* Start DMA data transfer. */
+ mov DFCNTRL, DMAPARAMS;
+dma_loop:
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ call idle_loop;
+ }
+ test SSTAT0,DMADONE jnz dma_dmadone;
+ test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */
+dma_phasemis:
+ /*
+ * We will be "done" DMAing when the transfer count goes to
+ * zero, or the target changes the phase (in light of this,
+ * it makes sense that the DMA circuitry doesn't ACK when
+ * PHASEMIS is active). If we are doing a SCSI->Host transfer,
+ * the data FIFO should be flushed auto-magically on STCNT=0
+ * or a phase change, so just wait for FIFO empty status.
+ */
+dma_checkfifo:
+ test DFCNTRL,DIRECTION jnz dma_fifoempty;
+dma_fifoflush:
+ test DFSTATUS,FIFOEMP jz dma_fifoflush;
+dma_fifoempty:
+ /* Don't clobber an inprogress host data transfer */
+ test DFSTATUS, MREQPEND jnz dma_fifoempty;
+
+ /*
+ * Now shut off the DMA and make sure that the DMA
+ * hardware has actually stopped. Touching the DMA
+ * counters, etc. while a DMA is active will result
+ * in an ILLSADDR exception.
+ */
+dma_dmadone:
+ and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+ /*
+ * Some revisions of the aic78XX have a problem where, if the
+ * data fifo is full, but the PCI input latch is not empty,
+ * HDMAEN cannot be cleared. The fix used here is to drain
+ * the prefetched but unused data from the data fifo until
+ * there is space for the input latch to drain.
+ */
+ if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+ mov NONE, DFDAT;
+ }
+ test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
+
+ /* See if we have completed this last segment */
+ test STCNT[0], 0xff jnz data_phase_finish;
+ test STCNT[1], 0xff jnz data_phase_finish;
+ test STCNT[2], 0xff jnz data_phase_finish;
+
+ /*
+ * Advance the scatter-gather pointers if needed
+ */
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+ && ahc->pci_cachesize != 0) {
+ test MWI_RESIDUAL, 0xFF jz no_mwi_resid;
+ /*
+ * Reload HADDR from SHADDR and setup the
+ * count to be the size of our residual.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov HADDR, SHADDR, 4;
+ mov HCNT, MWI_RESIDUAL;
+ bmov HCNT[1], ALLZEROS, 2;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SHADDR call bcopy_4;
+ mov MWI_RESIDUAL call set_hcnt;
+ }
+ clr MWI_RESIDUAL;
+ jmp sg_load_done;
+no_mwi_resid:
+ }
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz sg_load;
+ or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+ jmp data_phase_finish;
+sg_load:
+ /*
+ * Load the next SG element's data address and length
+ * into the DMA engine. If we don't have hardware
+ * to perform a prefetch, we'll have to fetch the
+ * segment from host memory first.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Wait for the idle loop to complete */
+ test CCSGCTL, CCSGEN jz . + 3;
+ call idle_loop;
+ test CCSGCTL, CCSGEN jnz . - 1;
+ bmov HADDR, CCSGRAM, 7;
+ /*
+ * Workaround for flaky external SCB RAM
+ * on certain aic7895 setups. It seems
+ * unable to handle direct transfers from
+ * S/G ram to certain SCB locations.
+ */
+ mov SINDEX, CCSGRAM;
+ mov SCB_RESIDUAL_DATACNT[3], SINDEX;
+ } else {
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ mov ALLZEROS call set_hhaddr;
+ }
+ mvi DINDEX, HADDR;
+ mvi SCB_RESIDUAL_SGPTR call bcopy_4;
+
+ mvi SG_SIZEOF call set_hcnt;
+
+ or DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+ call dma_finish;
+
+ mvi DINDEX, HADDR;
+ call dfdat_in_7;
+ mov SCB_RESIDUAL_DATACNT[3], DFDAT;
+ }
+
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ mov SCB_RESIDUAL_DATACNT[3] call set_hhaddr;
+
+ /*
+ * The lowest address byte must be loaded
+ * last as it triggers the computation of
+ * some items in the PCI block. The ULTRA2
+ * chips do this on PRELOAD.
+ */
+ mov HADDR, HADDR;
+ }
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+ && ahc->pci_cachesize != 0) {
+ call calc_mwi_residual;
+ }
+
+ /* Point to the new next sg in memory */
+ call sg_advance;
+
+sg_load_done:
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT, HCNT, 3;
+ } else {
+ call set_stcnt_from_hcnt;
+ }
+ /* Track odd'ness */
+ test HCNT[0], 0x1 jz . + 2;
+ xor DATA_COUNT_ODD, 0x1;
+
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jnz data_phase_loop;
+ }
+ }
+data_phase_finish:
+ /*
+ * If the target has left us in data phase, loop through
+ * the dma code again. In the case of ULTRA2 adapters,
+ * we should only loop if there is a data overrun. For
+ * all other adapters, we'll loop after each S/G element
+ * is loaded as well as if there is an overrun.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jnz data_phase_done;
+ }
+ if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+ test SSTAT1, REQINIT jz .;
+ if ((ahc->features & AHC_DT) == 0) {
+ test SSTAT1,PHASEMIS jz data_phase_loop;
+ } else {
+ test SCSIPHASE, DATA_PHASE_MASK jnz data_phase_loop;
+ }
+ }
+
+data_phase_done:
+ /*
+ * After a DMA finishes, save the SG and STCNT residuals back into
+ * the SCB. We use STCNT instead of HCNT, since it's a reflection
+ * of how many bytes were transferred on the SCSI (as opposed to the
+ * host) bus.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Kill off any pending prefetch */
+ call disable_ccsgen;
+ }
+
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ /*
+ * Clear the high address byte so that all other DMA
+ * operations, which use 32bit addressing, can assume
+ * HHADDR is 0.
+ */
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ mov ALLZEROS call set_hhaddr;
+ }
+ }
+
+ /*
+ * Update our residual information before the information is
+ * lost by some other type of SCSI I/O (e.g. PIO). If we have
+ * transferred all data, no update is needed.
+ *
+ */
+ test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jnz residual_update_done;
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+ && ahc->pci_cachesize != 0) {
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ test MWI_RESIDUAL, 0xFF jz bmov_resid;
+ }
+ mov A, MWI_RESIDUAL;
+ add SCB_RESIDUAL_DATACNT[0], A, STCNT[0];
+ clr A;
+ adc SCB_RESIDUAL_DATACNT[1], A, STCNT[1];
+ adc SCB_RESIDUAL_DATACNT[2], A, STCNT[2];
+ clr MWI_RESIDUAL;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ jmp . + 2;
+bmov_resid:
+ bmov SCB_RESIDUAL_DATACNT, STCNT, 3;
+ }
+ } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_RESIDUAL_DATACNT, STCNT, 3;
+ } else {
+ mov SCB_RESIDUAL_DATACNT[0], STCNT[0];
+ mov SCB_RESIDUAL_DATACNT[1], STCNT[1];
+ mov SCB_RESIDUAL_DATACNT[2], STCNT[2];
+ }
+residual_update_done:
+ /*
+ * Since we've been through a data phase, the SCB_RESID* fields
+ * are now initialized. Clear the full residual flag.
+ */
+ and SCB_SGPTR[0], ~SG_FULL_RESID;
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ /* Clear the channel in case we return to data phase later */
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ }
+
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SEQ_FLAGS, DPHASE_PENDING jz ITloop;
+ and SEQ_FLAGS, ~DPHASE_PENDING;
+ /*
+ * For data-in phases, wait for any pending acks from the
+ * initiator before changing phase. We only need to
+ * send Ignore Wide Residue messages for data-in phases.
+ */
+ test DFCNTRL, DIRECTION jz target_ITloop;
+ test SSTAT1, REQINIT jnz .;
+ test DATA_COUNT_ODD, 0x1 jz target_ITloop;
+ test SCSIRATE, WIDEXFER jz target_ITloop;
+ /*
+ * Issue an Ignore Wide Residue Message.
+ */
+ mvi P_MESGIN|BSYO call change_phase;
+ mvi MSG_IGN_WIDE_RESIDUE call target_outb;
+ mvi 1 call target_outb;
+ jmp target_ITloop;
+ } else {
+ jmp ITloop;
+ }
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Command phase. Set up the DMA registers and let 'er rip.
+ */
+p_command:
+ test SEQ_FLAGS,IDENTIFY_SEEN jnz p_command_okay;
+ mvi NO_IDENT jmp set_seqint;
+p_command_okay:
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov HCNT[0], SCB_CDB_LEN, 1;
+ bmov HCNT[1], ALLZEROS, 2;
+ mvi SG_CACHE_PRE, LAST_SEG;
+ } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT[0], SCB_CDB_LEN, 1;
+ bmov STCNT[1], ALLZEROS, 2;
+ } else {
+ mov STCNT[0], SCB_CDB_LEN;
+ clr STCNT[1];
+ clr STCNT[2];
+ }
+ add NONE, -13, SCB_CDB_LEN;
+ mvi SCB_CDB_STORE jnc p_command_embedded;
+p_command_from_host:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov HADDR[0], SCB_CDB_PTR, 4;
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+ } else {
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov HADDR[0], SCB_CDB_PTR, 4;
+ bmov HCNT, STCNT, 3;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCB_CDB_PTR call bcopy_4;
+ mov SCB_CDB_LEN call set_hcnt;
+ }
+ mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
+ }
+ jmp p_command_loop;
+p_command_embedded:
+ /*
+ * The data fifo seems to require 4 byte aligned
+ * transfers from the sequencer. Force this to
+ * be the case by clearing HADDR[0] even though
+ * we aren't going to touch host memory.
+ */
+ clr HADDR[0];
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
+ bmov DFDAT, SCB_CDB_STORE, 12;
+ } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ /*
+ * On the 7895 the data FIFO will
+ * get corrupted if you try to dump
+ * data from external SCB memory into
+ * the FIFO while it is enabled. So,
+ * fill the fifo and then enable SCSI
+ * transfers.
+ */
+ mvi DFCNTRL, (DIRECTION|FIFORESET);
+ } else {
+ mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+ }
+ bmov DFDAT, SCB_CDB_STORE, 12;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFOFLUSH);
+ } else {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ } else {
+ mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+ call copy_to_fifo_6;
+ call copy_to_fifo_6;
+ or DFCNTRL, FIFOFLUSH;
+ }
+p_command_loop:
+ if ((ahc->features & AHC_DT) == 0) {
+ test SSTAT0, SDONE jnz . + 2;
+ test SSTAT1, PHASEMIS jz p_command_loop;
+ /*
+ * Wait for our ACK to go-away on it's own
+ * instead of being killed by SCSIEN getting cleared.
+ */
+ test SCSISIGI, ACKI jnz .;
+ } else {
+ test DFCNTRL, SCSIEN jnz p_command_loop;
+ }
+ and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+ test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ /* Drop any residual from the S/G Preload queue */
+ or SXFRCTL0, CLRSTCNT;
+ }
+ jmp ITloop;
+
+/*
+ * Status phase. Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+p_status:
+ test SEQ_FLAGS,IDENTIFY_SEEN jnz p_status_okay;
+ mvi NO_IDENT jmp set_seqint;
+p_status_okay:
+ mov SCB_SCSI_STATUS, SCSIDATL;
+ jmp ITloop;
+
+/*
+ * Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target. The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte. This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own. If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
+ *
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the host to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ *
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+ /* Turn on ATN for the retry */
+ if ((ahc->features & AHC_DT) == 0) {
+ or SCSISIGO, ATNO, LASTPHASE;
+ } else {
+ mvi SCSISIGO, ATNO;
+ }
+p_mesgout:
+ mov SINDEX, MSG_OUT;
+ cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+ test SCB_CONTROL,MK_MESSAGE jnz host_message_loop;
+p_mesgout_identify:
+ or SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN;
+ test SCB_CONTROL, DISCENB jnz . + 2;
+ and SINDEX, ~DISCENB;
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
+ */
+p_mesgout_tag:
+ test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
+ mov SCSIDATL, SINDEX; /* Send the identify message */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ mov SCB_TAG jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
+ */
+p_mesgout_from_host:
+ cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
+ jmp host_message_loop;
+
+p_mesgout_onebyte:
+ mvi CLRSINT1, CLRATNO;
+ mov SCSIDATL, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT je p_mesgout_retry;
+
+p_mesgout_done:
+ mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
+ mov LAST_MSG, MSG_OUT;
+ mvi MSG_OUT, MSG_NOOP; /* No message left */
+ jmp ITloop;
+
+/*
+ * Message in phase. Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+ mvi ACCUM call inb_first; /* read the 1st message byte */
+
+ test A,MSG_IDENTIFYFLAG jnz mesgin_identify;
+ cmp A,MSG_DISCONNECT je mesgin_disconnect;
+ cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs;
+ cmp ALLZEROS,A je mesgin_complete;
+ cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs;
+ cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_ign_wide_residue;
+ cmp A,MSG_NOOP je mesgin_done;
+
+/*
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine. To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop. Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
+ */
+host_message_loop:
+ mvi HOST_MSG_LOOP call set_seqint;
+ call phase_lock;
+ cmp RETURN_1, EXIT_MSG_LOOP je ITloop + 1;
+ jmp host_message_loop;
+
+mesgin_ign_wide_residue:
+if ((ahc->features & AHC_WIDE) != 0) {
+ test SCSIRATE, WIDEXFER jz mesgin_reject;
+ /* Pull the residue byte */
+ mvi ARG_1 call inb_next;
+ cmp ARG_1, 0x01 jne mesgin_reject;
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+ test DATA_COUNT_ODD, 0x1 jz mesgin_done;
+ mvi IGN_WIDE_RES call set_seqint;
+ jmp mesgin_done;
+}
+
+mesgin_reject:
+ mvi MSG_MESSAGE_REJECT call mk_mesg;
+mesgin_done:
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+ jmp ITloop;
+
+mesgin_complete:
+/*
+ * We received a "command complete" message. Put the SCB_TAG into the QOUTFIFO,
+ * and trigger a completion interrupt. Before doing so, check to see if there
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
+ * process this information. In the case of a non zero status byte, we
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved. If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command, requeue
+ * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
+ * RETURN_1 to SEND_SENSE.
+ */
+
+/*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte.
+ * Either way, the target should take us to message out phase
+ * and then attempt to complete the command again. We should use a
+ * critical section here to guard against a timeout triggering
+ * for this command and setting ATN while we are still processing
+ * the completion.
+ test SCSISIGI, ATNI jnz mesgin_done;
+ */
+
+/*
+ * See if we attempted to deliver a message but the target ingnored us.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz . + 2;
+ mvi MKMSG_FAILED call set_seqint;
+
+/*
+ * Check for residuals
+ */
+ test SCB_SGPTR, SG_LIST_NULL jnz check_status;/* No xfer */
+ test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
+ test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
+check_status:
+ test SCB_SCSI_STATUS,0xff jz complete; /* Good Status? */
+upload_scb:
+ or SCB_SGPTR, SG_RESID_VALID;
+ mvi DMAPARAMS, FIFORESET;
+ mov SCB_TAG call dma_scb;
+ test SCB_SCSI_STATUS, 0xff jz complete; /* Just a residual? */
+ mvi BAD_STATUS call set_seqint; /* let driver know */
+ cmp RETURN_1, SEND_SENSE jne complete;
+ call add_scb_to_free_list;
+ jmp await_busfree;
+complete:
+ mov SCB_TAG call complete_post;
+ jmp await_busfree;
+}
+
+complete_post:
+ /* Post the SCBID in SINDEX and issue an interrupt */
+ call add_scb_to_free_list;
+ mov ARG_1, SINDEX;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ mov A, SDSCB_QOFF;
+ } else {
+ mov A, QOUTPOS;
+ }
+ mvi QOUTFIFO_OFFSET call post_byte_setup;
+ mov ARG_1 call post_byte;
+ if ((ahc->features & AHC_QUEUE_REGS) == 0) {
+ inc QOUTPOS;
+ }
+ mvi INTSTAT,CMDCMPLT ret;
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Is it a disconnect message? Set a flag in the SCB to remind us
+ * and await the bus going free. If this is an untagged transaction
+ * store the SCB id for it in our untagged target table for lookup on
+ * a reselction.
+ */
+mesgin_disconnect:
+ /*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte
+ * or we want to abort this command. Either way, the target
+ * should take us to message out phase and then attempt to
+ * disconnect again.
+ * XXX - Wait for more testing.
+ test SCSISIGI, ATNI jnz mesgin_done;
+ */
+
+ or SCB_CONTROL,DISCONNECTED;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ call add_scb_to_disc_list;
+ }
+ test SCB_CONTROL, TAG_ENB jnz await_busfree;
+ mov ARG_1, SCB_TAG;
+ mov SAVED_LUN, SCB_LUN;
+ mov SCB_SCSIID call set_busy_target;
+ jmp await_busfree;
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them. This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ * Ack the message as soon as possible. For chips without S/G pipelining,
+ * we can only ack the message after SHADDR has been saved. On these
+ * chips, SHADDR increments with every bus transaction, even PIO.
+ */
+mesgin_sdptrs:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+ test SEQ_FLAGS, DPHASE jz ITloop;
+ } else {
+ test SEQ_FLAGS, DPHASE jz mesgin_done;
+ }
+
+ /*
+ * If we are asked to save our position at the end of the
+ * transfer, just mark us at the end rather than perform a
+ * full save.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz mesgin_sdptrs_full;
+ or SCB_SGPTR, SG_LIST_NULL;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ jmp ITloop;
+ } else {
+ jmp mesgin_done;
+ }
+
+mesgin_sdptrs_full:
+
+ /*
+ * The SCB_SGPTR becomes the next one we'll download,
+ * and the SCB_DATAPTR becomes the current SHADDR.
+ * Use the residual number since STCNT is corrupted by
+ * any message transfer.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_DATAPTR, SHADDR, 4;
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+ }
+ bmov SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8;
+ } else {
+ mvi DINDEX, SCB_DATAPTR;
+ mvi SHADDR call bcopy_4;
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+ mvi SCB_RESIDUAL_DATACNT call bcopy_8;
+ }
+ jmp ITloop;
+
+/*
+ * Restore pointers message? Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.
+ */
+mesgin_rdptrs:
+ and SEQ_FLAGS, ~DPHASE; /*
+ * We'll reload them
+ * the next time through
+ * the dataphase.
+ */
+ jmp mesgin_done;
+
+/*
+ * Index into our Busy Target table. SINDEX and DINDEX are modified
+ * upon return. SCBPTR may be modified by this action.
+ */
+set_busy_target:
+ shr DINDEX, 4, SINDEX;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mov SCBPTR, SAVED_LUN;
+ add DINDEX, SCB_64_BTT;
+ } else {
+ add DINDEX, BUSY_TARGETS;
+ }
+ mov DINDIR, ARG_1 ret;
+
+/*
+ * Identify message? For a reconnecting target, this tells us the lun
+ * that the reconnection is for - find the correct SCB and switch to it,
+ * clearing the "disconnected" bit so we don't "find" it by accident later.
+ */
+mesgin_identify:
+ /*
+ * Determine whether a target is using tagged or non-tagged
+ * transactions by first looking at the transaction stored in
+ * the busy target array. If there is no untagged transaction
+ * for this target or the transaction is for a different lun, then
+ * this must be an untagged transaction.
+ */
+ shr SINDEX, 4, SAVED_SCSIID;
+ and SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ add SINDEX, SCB_64_BTT;
+ mov SCBPTR, SAVED_LUN;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ add NONE, -SCB_64_BTT, SINDEX;
+ jc . + 2;
+ mvi INTSTAT, OUT_OF_RANGE;
+ nop;
+ add NONE, -(SCB_64_BTT + 16), SINDEX;
+ jnc . + 2;
+ mvi INTSTAT, OUT_OF_RANGE;
+ nop;
+ }
+ } else {
+ add SINDEX, BUSY_TARGETS;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ add NONE, -BUSY_TARGETS, SINDEX;
+ jc . + 2;
+ mvi INTSTAT, OUT_OF_RANGE;
+ nop;
+ add NONE, -(BUSY_TARGETS + 16), SINDEX;
+ jnc . + 2;
+ mvi INTSTAT, OUT_OF_RANGE;
+ nop;
+ }
+ }
+ mov ARG_1, SINDIR;
+ cmp ARG_1, SCB_LIST_NULL je snoop_tag;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mov ARG_1 call findSCB;
+ } else {
+ mov SCBPTR, ARG_1;
+ }
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ jmp setup_SCB_id_lun_okay;
+ } else {
+ /*
+ * We only allow one untagged command per-target
+ * at a time. So, if the lun doesn't match, look
+ * for a tag message.
+ */
+ mov A, SCB_LUN;
+ cmp SAVED_LUN, A je setup_SCB_id_lun_okay;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ /*
+ * findSCB removes the SCB from the
+ * disconnected list, so we must replace
+ * it there should this SCB be for another
+ * lun.
+ */
+ call cleanup_scb;
+ }
+ }
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB. With SCB paging, we must search for non-tagged
+ * transactions since the SCB may exist in any slot. If we're not
+ * using SCB paging, we can use the tag as the direct index to the
+ * SCB.
+ */
+snoop_tag:
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x80;
+ }
+ mov NONE,SCSIDATL; /* ACK Identify MSG */
+ call phase_lock;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x1;
+ }
+ cmp LASTPHASE, P_MESGIN jne not_found;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x2;
+ }
+ cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mvi ARG_1 call inb_next; /* tag value */
+ mov ARG_1 call findSCB;
+ } else {
+ mvi ARG_1 call inb_next; /* tag value */
+ mov SCBPTR, ARG_1;
+ }
+
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+setup_SCB:
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x4;
+ }
+ mov A, SCB_SCSIID;
+ cmp SAVED_SCSIID, A jne not_found_cleanup_scb;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x8;
+ }
+setup_SCB_id_okay:
+ mov A, SCB_LUN;
+ cmp SAVED_LUN, A jne not_found_cleanup_scb;
+setup_SCB_id_lun_okay:
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x10;
+ }
+ test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
+ and SCB_CONTROL,~DISCONNECTED;
+ test SCB_CONTROL, TAG_ENB jnz setup_SCB_tagged;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mov A, SCBPTR;
+ }
+ mvi ARG_1, SCB_LIST_NULL;
+ mov SAVED_SCSIID call set_busy_target;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mov SCBPTR, A;
+ }
+setup_SCB_tagged:
+ mvi SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */
+ call set_transfer_settings;
+ /* See if the host wants to send a message upon reconnection */
+ test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+ mvi HOST_MSG call mk_mesg;
+ jmp mesgin_done;
+
+not_found_cleanup_scb:
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ call cleanup_scb;
+ }
+not_found:
+ mvi NO_MATCH call set_seqint;
+ jmp mesgin_done;
+
+mk_mesg:
+ if ((ahc->features & AHC_DT) == 0) {
+ or SCSISIGO, ATNO, LASTPHASE;
+ } else {
+ mvi SCSISIGO, ATNO;
+ }
+ mov MSG_OUT,SINDEX ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * According to Adaptec's documentation, an ACK is not sent on input from
+ * the target until SCSIDATL is read from. So we wait until SCSIDATL is
+ * latched (the usual way), then read the data byte directly off the bus
+ * using SCSIBUSL. When we have pulled the ATN line, or we just want to
+ * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
+ * spec guarantees that the target will hold the data byte on the bus until
+ * we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called. inb_{first,next}
+ * use the same calling convention as inb.
+ */
+inb_next_wait_perr:
+ mvi PERR_DETECTED call set_seqint;
+ jmp inb_next_wait;
+inb_next:
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+inb_next_wait:
+ /*
+ * If there is a parity error, wait for the kernel to
+ * see the interrupt and prepare our message response
+ * before continuing.
+ */
+ test SSTAT1, REQINIT jz inb_next_wait;
+ test SSTAT1, SCSIPERR jnz inb_next_wait_perr;
+inb_next_check_phase:
+ and LASTPHASE, PHASE_MASK, SCSISIGI;
+ cmp LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+ mov DINDEX,SINDEX;
+ mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/
+inb_last:
+ mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/
+}
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * Change to a new phase. If we are changing the state of the I/O signal,
+ * from out to in, wait an additional data release delay before continuing.
+ */
+change_phase:
+ /* Wait for preceeding I/O session to complete. */
+ test SCSISIGI, ACKI jnz .;
+
+ /* Change the phase */
+ and DINDEX, IOI, SCSISIGI;
+ mov SCSISIGO, SINDEX;
+ and A, IOI, SINDEX;
+
+ /*
+ * If the data direction has changed, from
+ * out (initiator driving) to in (target driving),
+ * we must wait at least a data release delay plus
+ * the normal bus settle delay. [SCSI III SPI 10.11.0]
+ */
+ cmp DINDEX, A je change_phase_wait;
+ test SINDEX, IOI jz change_phase_wait;
+ call change_phase_wait;
+change_phase_wait:
+ nop;
+ nop;
+ nop;
+ nop ret;
+
+/*
+ * Send a byte to an initiator in Automatic PIO mode.
+ */
+target_outb:
+ or SXFRCTL0, SPIOEN;
+ test SSTAT0, SPIORDY jz .;
+ mov SCSIDATL, SINDEX;
+ test SSTAT0, SPIORDY jz .;
+ and SXFRCTL0, ~SPIOEN ret;
+}
+
+/*
+ * Locate a disconnected SCB by SCBID. Upon return, SCBPTR and SINDEX will
+ * be set to the position of the SCB. If the SCB cannot be found locally,
+ * it will be paged in from host memory. RETURN_2 stores the address of the
+ * preceding SCB in the disconnected list which can be used to speed up
+ * removal of the found SCB from the disconnected list.
+ */
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL;
+findSCB:
+ mov A, SINDEX; /* Tag passed in SINDEX */
+ cmp DISCONNECTED_SCBH, SCB_LIST_NULL je findSCB_notFound;
+ mov SCBPTR, DISCONNECTED_SCBH; /* Initialize SCBPTR */
+ mvi ARG_2, SCB_LIST_NULL; /* Head of list */
+ jmp findSCB_loop;
+findSCB_next:
+ cmp SCB_NEXT, SCB_LIST_NULL je findSCB_notFound;
+ mov ARG_2, SCBPTR;
+ mov SCBPTR,SCB_NEXT;
+findSCB_loop:
+ cmp SCB_TAG, A jne findSCB_next;
+rem_scb_from_disc_list:
+ cmp ARG_2, SCB_LIST_NULL je rHead;
+ mov DINDEX, SCB_NEXT;
+ mov SINDEX, SCBPTR;
+ mov SCBPTR, ARG_2;
+ mov SCB_NEXT, DINDEX;
+ mov SCBPTR, SINDEX ret;
+rHead:
+ mov DISCONNECTED_SCBH,SCB_NEXT ret;
+END_CRITICAL;
+findSCB_notFound:
+ /*
+ * We didn't find it. Page in the SCB.
+ */
+ mov ARG_1, A; /* Save tag */
+ mov ALLZEROS call get_free_or_disc_scb;
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ mov ARG_1 jmp dma_scb;
+}
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SHARED_DATA_ADDR.
+ */
+post_byte_setup:
+ mov ARG_2, SINDEX;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi SHARED_DATA_ADDR call set_1byte_addr;
+ mvi CCHCNT, 1;
+ mvi CCSCBCTL, CCSCBRESET ret;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SHARED_DATA_ADDR call set_1byte_addr;
+ mvi 1 call set_hcnt;
+ mvi DFCNTRL, FIFORESET ret;
+ }
+
+post_byte:
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov CCSCBRAM, SINDEX, 1;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ clr CCSCBCTL ret;
+ } else {
+ mov DFDAT, SINDEX;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ jmp dma_finish;
+ }
+
+phase_lock_perr:
+ mvi PERR_DETECTED call set_seqint;
+phase_lock:
+ /*
+ * If there is a parity error, wait for the kernel to
+ * see the interrupt and prepare our message response
+ * before continuing.
+ */
+ test SSTAT1, REQINIT jz phase_lock;
+ test SSTAT1, SCSIPERR jnz phase_lock_perr;
+phase_lock_latch_phase:
+ if ((ahc->features & AHC_DT) == 0) {
+ and SCSISIGO, PHASE_MASK, SCSISIGI;
+ }
+ and LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+if ((ahc->features & AHC_CMD_CHAN) == 0) {
+set_hcnt:
+ mov HCNT[0], SINDEX;
+clear_hcnt:
+ clr HCNT[1];
+ clr HCNT[2] ret;
+
+set_stcnt_from_hcnt:
+ mov STCNT[0], HCNT[0];
+ mov STCNT[1], HCNT[1];
+ mov STCNT[2], HCNT[2] ret;
+
+bcopy_8:
+ mov DINDIR, SINDIR;
+bcopy_7:
+ mov DINDIR, SINDIR;
+ mov DINDIR, SINDIR;
+bcopy_5:
+ mov DINDIR, SINDIR;
+bcopy_4:
+ mov DINDIR, SINDIR;
+bcopy_3:
+ mov DINDIR, SINDIR;
+ mov DINDIR, SINDIR;
+ mov DINDIR, SINDIR ret;
+}
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_32byte_addr:
+ shr ARG_2, 3, A;
+ shl A, 5;
+ jmp set_1byte_addr;
+}
+
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 64byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_64byte_addr:
+ shr ARG_2, 2, A;
+ shl A, 6;
+
+/*
+ * Setup addr assuming that A + (ARG_2 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+ add DINDIR, A, SINDIR;
+ mov A, ARG_2;
+ adc DINDIR, A, SINDIR;
+ clr A;
+ adc DINDIR, A, SINDIR;
+ adc DINDIR, A, SINDIR ret;
+
+/*
+ * Either post or fetch an SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
+dma_scb:
+ mov A, SINDEX;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi HSCB_ADDR call set_64byte_addr;
+ mov CCSCBPTR, SCBPTR;
+ test DMAPARAMS, DIRECTION jz dma_scb_tohost;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mvi CCHCNT, SCB_DOWNLOAD_SIZE_64;
+ } else {
+ mvi CCHCNT, SCB_DOWNLOAD_SIZE;
+ }
+ mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+ jmp dma_scb_finish;
+dma_scb_tohost:
+ mvi CCHCNT, SCB_UPLOAD_SIZE;
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ mvi CCSCBCTL, CCSCBRESET;
+ bmov CCSCBRAM, SCB_BASE, SCB_UPLOAD_SIZE;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ } else if ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0) {
+ mvi CCSCBCTL, CCARREN|CCSCBRESET;
+ cmp CCSCBCTL, ARRDONE|CCARREN jne .;
+ mvi CCHCNT, SCB_UPLOAD_SIZE;
+ mvi CCSCBCTL, CCSCBEN|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
+ } else {
+ mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+ }
+dma_scb_finish:
+ clr CCSCBCTL;
+ test CCSCBCTL, CCARREN|CCSCBEN jnz .;
+ ret;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi HSCB_ADDR call set_64byte_addr;
+ mvi SCB_DOWNLOAD_SIZE call set_hcnt;
+ mov DFCNTRL, DMAPARAMS;
+ test DMAPARAMS, DIRECTION jnz dma_scb_fromhost;
+ /* Fill it with the SCB data */
+copy_scb_tofifo:
+ mvi SINDEX, SCB_BASE;
+ add A, SCB_DOWNLOAD_SIZE, SINDEX;
+copy_scb_tofifo_loop:
+ call copy_to_fifo_8;
+ cmp SINDEX, A jne copy_scb_tofifo_loop;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ jmp dma_finish;
+dma_scb_fromhost:
+ mvi DINDEX, SCB_BASE;
+ if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+ /*
+ * The PCI module will only issue a PCI
+ * retry if the data FIFO is empty. If the
+ * host disconnects in the middle of a
+ * transfer, we must empty the fifo of all
+ * available data to force the chip to
+ * continue the transfer. This does not
+ * happen for SCSI transfers as the SCSI module
+ * will drain the FIFO as data is made available.
+ * When the hang occurs, we know that a multiple
+ * of 8 bytes are in the FIFO because the PCI
+ * module has an 8 byte input latch that only
+ * dumps to the FIFO when HCNT == 0 or the
+ * latch is full.
+ */
+ clr A;
+ /* Wait for at least 8 bytes of data to arrive. */
+dma_scb_hang_fifo:
+ test DFSTATUS, FIFOQWDEMP jnz dma_scb_hang_fifo;
+dma_scb_hang_wait:
+ test DFSTATUS, MREQPEND jnz dma_scb_hang_wait;
+ test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+ test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+ test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+ /*
+ * The PCI module no longer intends to perform
+ * a PCI transaction. Drain the fifo.
+ */
+dma_scb_hang_dma_drain_fifo:
+ not A, HCNT;
+ add A, SCB_DOWNLOAD_SIZE+SCB_BASE+1;
+ and A, ~0x7;
+ mov DINDIR,DFDAT;
+ cmp DINDEX, A jne . - 1;
+ cmp DINDEX, SCB_DOWNLOAD_SIZE+SCB_BASE
+ je dma_finish_nowait;
+ /* Restore A as the lines left to transfer. */
+ add A, -SCB_BASE, DINDEX;
+ shr A, 3;
+ jmp dma_scb_hang_fifo;
+dma_scb_hang_dma_done:
+ and DFCNTRL, ~HDMAEN;
+ test DFCNTRL, HDMAEN jnz .;
+ add SEQADDR0, A;
+ } else {
+ call dma_finish;
+ }
+ /* If we were putting the SCB, we are done */
+ call dfdat_in_8;
+ call dfdat_in_8;
+ call dfdat_in_8;
+dfdat_in_8:
+ mov DINDIR,DFDAT;
+dfdat_in_7:
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+dfdat_in_2:
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT ret;
+ }
+
+copy_to_fifo_8:
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+copy_to_fifo_6:
+ mov DFDAT,SINDIR;
+copy_to_fifo_5:
+ mov DFDAT,SINDIR;
+copy_to_fifo_4:
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR ret;
+
+/*
+ * Wait for DMA from host memory to data FIFO to complete, then disable
+ * DMA and wait for it to acknowledge that it's off.
+ */
+dma_finish:
+ test DFSTATUS,HDONE jz dma_finish;
+dma_finish_nowait:
+ /* Turn off DMA */
+ and DFCNTRL, ~HDMAEN;
+ test DFCNTRL, HDMAEN jnz .;
+ ret;
+
+/*
+ * Restore an SCB that failed to match an incoming reselection
+ * to the correct/safe state. If the SCB is for a disconnected
+ * transaction, it must be returned to the disconnected list.
+ * If it is not in the disconnected state, it must be free.
+ */
+cleanup_scb:
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ test SCB_CONTROL,DISCONNECTED jnz add_scb_to_disc_list;
+ }
+add_scb_to_free_list:
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL;
+ mov SCB_NEXT, FREE_SCBH;
+ mvi SCB_TAG, SCB_LIST_NULL;
+ mov FREE_SCBH, SCBPTR ret;
+END_CRITICAL;
+ } else {
+ mvi SCB_TAG, SCB_LIST_NULL ret;
+ }
+
+if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+set_hhaddr:
+ or DSCOMMAND1, HADDLDSEL0;
+ and HADDR, SG_HIGH_ADDR_BITS, SINDEX;
+ and DSCOMMAND1, ~HADDLDSEL0 ret;
+}
+
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
+get_free_or_disc_scb:
+BEGIN_CRITICAL;
+ cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
+ cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
+return_error:
+ mvi NO_FREE_SCB call set_seqint;
+ mvi SINDEX, SCB_LIST_NULL ret;
+dequeue_disc_scb:
+ mov SCBPTR, DISCONNECTED_SCBH;
+ mov DISCONNECTED_SCBH, SCB_NEXT;
+END_CRITICAL;
+ mvi DMAPARAMS, FIFORESET;
+ mov SCB_TAG jmp dma_scb;
+BEGIN_CRITICAL;
+dequeue_free_scb:
+ mov SCBPTR, FREE_SCBH;
+ mov FREE_SCBH, SCB_NEXT ret;
+END_CRITICAL;
+
+add_scb_to_disc_list:
+/*
+ * Link this SCB into the DISCONNECTED list. This list holds the
+ * candidates for paging out an SCB if one is needed for a new command.
+ * Modifying the disconnected list is a critical(pause dissabled) section.
+ */
+BEGIN_CRITICAL;
+ mov SCB_NEXT, DISCONNECTED_SCBH;
+ mov DISCONNECTED_SCBH, SCBPTR ret;
+END_CRITICAL;
+}
+set_seqint:
+ mov INTSTAT, SINDEX;
+ nop;
+return:
+ ret;
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.c b/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
new file mode 100644
index 0000000000..0ae6bdebef
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
@@ -0,0 +1,306 @@
+/*
+ * Interface for the 93C66/56/46/26/06 serial eeprom parts.
+ *
+ * Copyright (c) 1995, 1996 Daniel M. Eischen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.c#15 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_93cx6.c,v 1.8.2.5 2002/04/29 19:36:31 gibbs Exp $
+ */
+
+/*
+ * The instruction set of the 93C66/56/46/26/06 chips are as follows:
+ *
+ * Start OP *
+ * Function Bit Code Address** Data Description
+ * -------------------------------------------------------------------
+ * READ 1 10 A5 - A0 Reads data stored in memory,
+ * starting at specified address
+ * EWEN 1 00 11XXXX Write enable must precede
+ * all programming modes
+ * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0
+ * WRITE 1 01 A5 - A0 D15 - D0 Writes register
+ * ERAL 1 00 10XXXX Erase all registers
+ * WRAL 1 00 01XXXX D15 - D0 Writes to all registers
+ * EWDS 1 00 00XXXX Disables all programming
+ * instructions
+ * *Note: A value of X for address is a don't care condition.
+ * **Note: There are 8 address bits for the 93C56/66 chips unlike
+ * the 93C46/26/06 chips which have 6 address bits.
+ *
+ * The 93C46 has a four wire interface: clock, chip select, data in, and
+ * data out. In order to perform one of the above functions, you need
+ * to enable the chip select for a clock period (typically a minimum of
+ * 1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ * respectively). While the chip select remains high, you can clock in
+ * the instructions (above) starting with the start bit, followed by the
+ * OP code, Address, and Data (if needed). For the READ instruction, the
+ * requested 16-bit register contents is read from the data out line but
+ * is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ * first). The clock cycling from low to high initiates the next data
+ * bit to be sent from the chip.
+ *
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+/*
+ * Right now, we only have to read the SEEPROM. But we make it easier to
+ * add other 93Cx6 functions.
+ */
+static struct seeprom_cmd {
+ uint8_t len;
+ uint8_t bits[9];
+} seeprom_read = {3, {1, 1, 0}};
+
+static struct seeprom_cmd seeprom_ewen = {9, {1, 0, 0, 1, 1, 0, 0, 0, 0}};
+static struct seeprom_cmd seeprom_ewds = {9, {1, 0, 0, 0, 0, 0, 0, 0, 0}};
+static struct seeprom_cmd seeprom_write = {3, {1, 0, 1}};
+
+/*
+ * Wait for the SEERDY to go high; about 800 ns.
+ */
+#define CLOCK_PULSE(sd, rdy) \
+ while ((SEEPROM_STATUS_INB(sd) & rdy) == 0) { \
+ ; /* Do nothing */ \
+ } \
+ (void)SEEPROM_INB(sd); /* Clear clock */
+
+/*
+ * Send a START condition and the given command
+ */
+static void
+send_seeprom_cmd(struct seeprom_descriptor *sd, struct seeprom_cmd *cmd)
+{
+ uint8_t temp;
+ int i = 0;
+
+ /* Send chip select for one clock cycle. */
+ temp = sd->sd_MS ^ sd->sd_CS;
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+
+ for (i = 0; i < cmd->len; i++) {
+ if (cmd->bits[i] != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if (cmd->bits[i] != 0)
+ temp ^= sd->sd_DO;
+ }
+}
+
+/*
+ * Clear CS put the chip in the reset state, where it can wait for new commands.
+ */
+static void
+reset_seeprom(struct seeprom_descriptor *sd)
+{
+ uint8_t temp;
+
+ temp = sd->sd_MS;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+}
+
+/*
+ * Read the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ */
+int
+ahc_read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count)
+{
+ int i = 0;
+ u_int k = 0;
+ uint16_t v;
+ uint8_t temp;
+
+ /*
+ * Read the requested registers of the seeprom. The loop
+ * will range from 0 to count-1.
+ */
+ for (k = start_addr; k < count + start_addr; k++) {
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ send_seeprom_cmd(sd, &seeprom_read);
+
+ /* Send the 6 or 8 bit address (MSB first, LSB last). */
+ temp = sd->sd_MS ^ sd->sd_CS;
+ for (i = (sd->sd_chip - 1); i >= 0; i--) {
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ v = 0;
+ for (i = 16; i >= 0; i--) {
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ v <<= 1;
+ if (SEEPROM_DATA_INB(sd) & sd->sd_DI)
+ v |= 1;
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ }
+
+ buf[k - start_addr] = v;
+
+ /* Reset the chip select for the next command cycle. */
+ reset_seeprom(sd);
+ }
+#ifdef AHC_DUMP_EEPROM
+ printf("\nSerial EEPROM:\n\t");
+ for (k = 0; k < count; k = k + 1) {
+ if (((k % 8) == 0) && (k != 0)) {
+ printf ("\n\t");
+ }
+ printf (" 0x%x", buf[k]);
+ }
+ printf ("\n");
+#endif
+ return (1);
+}
+
+/*
+ * Write the serial EEPROM and return 1 if successful and 0 if
+ * not successful.
+ */
+int
+ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count)
+{
+ uint16_t v;
+ uint8_t temp;
+ int i, k;
+
+ /* Place the chip into write-enable mode */
+ send_seeprom_cmd(sd, &seeprom_ewen);
+ reset_seeprom(sd);
+
+ /* Write all requested data out to the seeprom. */
+ temp = sd->sd_MS ^ sd->sd_CS;
+ for (k = start_addr; k < count + start_addr; k++) {
+ /* Send the write command */
+ send_seeprom_cmd(sd, &seeprom_write);
+
+ /* Send the 6 or 8 bit address (MSB first). */
+ for (i = (sd->sd_chip - 1); i >= 0; i--) {
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ }
+
+ /* Write the 16 bit value, MSB first */
+ v = buf[k - start_addr];
+ for (i = 15; i >= 0; i--) {
+ if ((v & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if ((v & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ }
+
+ /* Wait for the chip to complete the write */
+ temp = sd->sd_MS;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ temp = sd->sd_MS ^ sd->sd_CS;
+ do {
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ } while ((SEEPROM_DATA_INB(sd) & sd->sd_DI) == 0);
+
+ reset_seeprom(sd);
+ }
+
+ /* Put the chip back into write-protect mode */
+ send_seeprom_cmd(sd, &seeprom_ewds);
+ reset_seeprom(sd);
+
+ return (1);
+}
+
+int
+ahc_verify_cksum(struct seeprom_config *sc)
+{
+ int i;
+ int maxaddr;
+ uint32_t checksum;
+ uint16_t *scarray;
+
+ maxaddr = (sizeof(*sc)/2) - 1;
+ checksum = 0;
+ scarray = (uint16_t *)sc;
+
+ for (i = 0; i < maxaddr; i++)
+ checksum = checksum + scarray[i];
+ if (checksum == 0
+ || (checksum & 0xFFFF) != sc->checksum) {
+ return (0);
+ } else {
+ return(1);
+ }
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.h b/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
new file mode 100644
index 0000000000..9535c7283f
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
@@ -0,0 +1,102 @@
+/*
+ * Interface to the 93C46/56 serial EEPROM that is used to store BIOS
+ * settings for the aic7xxx based adaptec SCSI controllers. It can
+ * also be used for 93C26 and 93C06 serial EEPROMS.
+ *
+ * Copyright (c) 1994, 1995, 2000 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.h#10 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_93cx6.h,v 1.7.2.3 2002/04/29 19:36:31 gibbs Exp $
+ */
+#ifndef _AIC7XXX_93CX6_H_
+#define _AIC7XXX_93CX6_H_
+
+typedef enum {
+ C46 = 6,
+ C56_66 = 8
+} seeprom_chip_t;
+
+struct seeprom_descriptor {
+ struct ahc_softc *sd_ahc;
+ u_int sd_control_offset;
+ u_int sd_status_offset;
+ u_int sd_dataout_offset;
+ seeprom_chip_t sd_chip;
+ uint16_t sd_MS;
+ uint16_t sd_RDY;
+ uint16_t sd_CS;
+ uint16_t sd_CK;
+ uint16_t sd_DO;
+ uint16_t sd_DI;
+};
+
+/*
+ * This function will read count 16-bit words from the serial EEPROM and
+ * return their value in buf. The port address of the aic7xxx serial EEPROM
+ * control register is passed in as offset. The following parameters are
+ * also passed in:
+ *
+ * CS - Chip select
+ * CK - Clock
+ * DO - Data out
+ * DI - Data in
+ * RDY - SEEPROM ready
+ * MS - Memory port mode select
+ *
+ * A failed read attempt returns 0, and a successful read returns 1.
+ */
+
+#define SEEPROM_INB(sd) \
+ ahc_inb(sd->sd_ahc, sd->sd_control_offset)
+#define SEEPROM_OUTB(sd, value) \
+do { \
+ ahc_outb(sd->sd_ahc, sd->sd_control_offset, value); \
+ ahc_flush_device_writes(sd->sd_ahc); \
+} while(0)
+
+#define SEEPROM_STATUS_INB(sd) \
+ ahc_inb(sd->sd_ahc, sd->sd_status_offset)
+#define SEEPROM_DATA_INB(sd) \
+ ahc_inb(sd->sd_ahc, sd->sd_dataout_offset)
+
+int ahc_read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count);
+int ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count);
+int ahc_verify_cksum(struct seeprom_config *sc);
+
+#endif /* _AIC7XXX_93CX6_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_core.c b/xen/drivers/scsi/aic7xxx/aic7xxx_core.c
new file mode 100644
index 0000000000..ebbf161d08
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_core.c
@@ -0,0 +1,7127 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#69 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.c,v 1.41.2.22 2002/04/29 19:36:26 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
+#endif
+
+/****************************** Softc Data ************************************/
+struct ahc_softc_tailq ahc_tailq = TAILQ_HEAD_INITIALIZER(ahc_tailq);
+
+/***************************** Lookup Tables **********************************/
+char *ahc_chip_names[] =
+{
+ "NONE",
+ "aic7770",
+ "aic7850",
+ "aic7855",
+ "aic7859",
+ "aic7860",
+ "aic7870",
+ "aic7880",
+ "aic7895",
+ "aic7895C",
+ "aic7890/91",
+ "aic7896/97",
+ "aic7892",
+ "aic7899"
+};
+static const u_int num_chip_names = NUM_ELEMENTS(ahc_chip_names);
+
+/*
+ * Hardware error codes.
+ */
+struct ahc_hard_error_entry {
+ uint8_t errno;
+ char *errmesg;
+};
+
+static struct ahc_hard_error_entry ahc_hard_errors[] = {
+ { ILLHADDR, "Illegal Host Access" },
+ { ILLSADDR, "Illegal Sequencer Address referrenced" },
+ { ILLOPCODE, "Illegal Opcode in sequencer program" },
+ { SQPARERR, "Sequencer Parity Error" },
+ { DPARERR, "Data-path Parity Error" },
+ { MPARERR, "Scratch or SCB Memory Parity Error" },
+ { PCIERRSTAT, "PCI Error detected" },
+ { CIOPARERR, "CIOBUS Parity Error" },
+};
+static const u_int num_errors = NUM_ELEMENTS(ahc_hard_errors);
+
+static struct ahc_phase_table_entry ahc_phase_table[] =
+{
+ { P_DATAOUT, MSG_NOOP, "in Data-out phase" },
+ { P_DATAIN, MSG_INITIATOR_DET_ERR, "in Data-in phase" },
+ { P_DATAOUT_DT, MSG_NOOP, "in DT Data-out phase" },
+ { P_DATAIN_DT, MSG_INITIATOR_DET_ERR, "in DT Data-in phase" },
+ { P_COMMAND, MSG_NOOP, "in Command phase" },
+ { P_MESGOUT, MSG_NOOP, "in Message-out phase" },
+ { P_STATUS, MSG_INITIATOR_DET_ERR, "in Status phase" },
+ { P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
+ { P_BUSFREE, MSG_NOOP, "while idle" },
+ { 0, MSG_NOOP, "in unknown phase" }
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = NUM_ELEMENTS(ahc_phase_table) - 1;
+
+/*
+ * Valid SCSIRATE values. (p. 3-17)
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsixfer reg.
+ */
+static struct ahc_syncrate ahc_syncrates[] =
+{
+ /* ultra2 fast/ultra period rate */
+ { 0x42, 0x000, 9, "80.0" },
+ { 0x03, 0x000, 10, "40.0" },
+ { 0x04, 0x000, 11, "33.0" },
+ { 0x05, 0x100, 12, "20.0" },
+ { 0x06, 0x110, 15, "16.0" },
+ { 0x07, 0x120, 18, "13.4" },
+ { 0x08, 0x000, 25, "10.0" },
+ { 0x19, 0x010, 31, "8.0" },
+ { 0x1a, 0x020, 37, "6.67" },
+ { 0x1b, 0x030, 43, "5.7" },
+ { 0x1c, 0x040, 50, "5.0" },
+ { 0x00, 0x050, 56, "4.4" },
+ { 0x00, 0x060, 62, "4.0" },
+ { 0x00, 0x070, 68, "3.6" },
+ { 0x00, 0x000, 0, NULL }
+};
+
+/* Our Sequencer Program */
+#include "aic7xxx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static void ahc_force_renegotiation(struct ahc_softc *ahc);
+static struct ahc_tmode_tstate*
+ ahc_alloc_tstate(struct ahc_softc *ahc,
+ u_int scsi_id, char channel);
+#ifdef AHC_TARGET_MODE
+static void ahc_free_tstate(struct ahc_softc *ahc,
+ u_int scsi_id, char channel, int force);
+#endif
+static struct ahc_syncrate*
+ ahc_devlimited_syncrate(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *,
+ u_int *period,
+ u_int *ppr_options,
+ role_t role);
+static void ahc_update_pending_scbs(struct ahc_softc *ahc);
+static void ahc_fetch_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_scb_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb);
+static void ahc_assert_atn(struct ahc_softc *ahc);
+static void ahc_setup_initiator_msgout(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb);
+static void ahc_build_transfer_msg(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_construct_sdtr(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int period, u_int offset);
+static void ahc_construct_wdtr(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int bus_width);
+static void ahc_construct_ppr(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int period, u_int offset,
+ u_int bus_width, u_int ppr_options);
+static void ahc_clear_msg_state(struct ahc_softc *ahc);
+static void ahc_handle_message_phase(struct ahc_softc *ahc);
+typedef enum {
+ AHCMSG_1B,
+ AHCMSG_2B,
+ AHCMSG_EXT
+} ahc_msgtype;
+static int ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type,
+ u_int msgval, int full);
+static int ahc_parse_msg(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static int ahc_handle_msg_reject(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_handle_ign_wide_residue(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_reinitialize_dataptrs(struct ahc_softc *ahc);
+static void ahc_handle_devreset(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ cam_status status, char *message,
+ int verbose_level);
+#if AHC_TARGET_MODE
+static void ahc_setup_target_msgin(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb);
+#endif
+
+static bus_dmamap_callback_t ahc_dmamap_cb;
+static void ahc_build_free_scb_list(struct ahc_softc *ahc);
+static int ahc_init_scbdata(struct ahc_softc *ahc);
+static void ahc_fini_scbdata(struct ahc_softc *ahc);
+static void ahc_qinfifo_requeue(struct ahc_softc *ahc,
+ struct scb *prev_scb,
+ struct scb *scb);
+static int ahc_qinfifo_count(struct ahc_softc *ahc);
+static u_int ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
+ u_int prev, u_int scbptr);
+static void ahc_add_curscb_to_free_list(struct ahc_softc *ahc);
+static u_int ahc_rem_wscb(struct ahc_softc *ahc,
+ u_int scbpos, u_int prev);
+static void ahc_reset_current_bus(struct ahc_softc *ahc);
+#ifdef AHC_DUMP_SEQ
+static void ahc_dumpseq(struct ahc_softc *ahc);
+#endif
+static void ahc_loadseq(struct ahc_softc *ahc);
+static int ahc_check_patch(struct ahc_softc *ahc,
+ struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr);
+static void ahc_download_instr(struct ahc_softc *ahc,
+ u_int instrptr, uint8_t *dconsts);
+#ifdef AHC_TARGET_MODE
+static void ahc_queue_lstate_event(struct ahc_softc *ahc,
+ struct ahc_tmode_lstate *lstate,
+ u_int initiator_id,
+ u_int event_type,
+ u_int event_arg);
+static void ahc_update_scsiid(struct ahc_softc *ahc,
+ u_int targid_mask);
+static int ahc_handle_target_cmd(struct ahc_softc *ahc,
+ struct target_cmd *cmd);
+#endif
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+void
+ahc_restart(struct ahc_softc *ahc)
+{
+
+ ahc_pause(ahc);
+
+ /* No more pending messages. */
+ ahc_clear_msg_state(ahc);
+
+ ahc_outb(ahc, SCSISIGO, 0); /* De-assert BSY */
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP); /* No message to send */
+ ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+ ahc_outb(ahc, LASTPHASE, P_BUSFREE);
+ ahc_outb(ahc, SAVED_SCSIID, 0xFF);
+ ahc_outb(ahc, SAVED_LUN, 0xFF);
+
+ /*
+ * Ensure that the sequencer's idea of TQINPOS
+ * matches our own. The sequencer increments TQINPOS
+ * only after it sees a DMA complete and a reset could
+ * occur before the increment leaving the kernel to believe
+ * the command arrived but the sequencer to not.
+ */
+ ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+
+ /* Always allow reselection */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Ensure that no DMA operations are in progress */
+ ahc_outb(ahc, CCSCBCNT, 0);
+ ahc_outb(ahc, CCSGCTL, 0);
+ ahc_outb(ahc, CCSCBCTL, 0);
+ }
+ /*
+ * If we were in the process of DMA'ing SCB data into
+ * an SCB, replace that SCB on the free list. This prevents
+ * an SCB leak.
+ */
+ if ((ahc_inb(ahc, SEQ_FLAGS2) & SCB_DMA) != 0) {
+ ahc_add_curscb_to_free_list(ahc);
+ ahc_outb(ahc, SEQ_FLAGS2,
+ ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
+ }
+ ahc_outb(ahc, MWI_RESIDUAL, 0);
+ ahc_outb(ahc, SEQCTL, FASTMODE);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+ ahc_unpause(ahc);
+}
+
+/************************* Input/Output Queues ********************************/
+void
+ahc_run_qoutfifo(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+ u_int scb_index;
+
+ ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+ while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
+
+ scb_index = ahc->qoutfifo[ahc->qoutfifonext];
+ if ((ahc->qoutfifonext & 0x03) == 0x03) {
+ u_int modnext;
+
+ /*
+ * Clear 32bits of QOUTFIFO at a time
+ * so that we don't clobber an incoming
+ * byte DMA to the array on architectures
+ * that only support 32bit load and store
+ * operations.
+ */
+ modnext = ahc->qoutfifonext & ~0x3;
+ *((uint32_t *)(&ahc->qoutfifo[modnext])) = 0xFFFFFFFFUL;
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap,
+ /*offset*/modnext, /*len*/4,
+ BUS_DMASYNC_PREREAD);
+ }
+ ahc->qoutfifonext++;
+
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("%s: WARNING no command for scb %d "
+ "(cmdcmplt)\nQOUTPOS = %d\n",
+ ahc_name(ahc), scb_index,
+ ahc->qoutfifonext - 1);
+ continue;
+ }
+
+ /*
+ * Save off the residual
+ * if there is one.
+ */
+ ahc_update_residual(ahc, scb);
+ ahc_done(ahc, scb);
+ }
+}
+
+void
+ahc_run_untagged_queues(struct ahc_softc *ahc)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ ahc_run_untagged_queue(ahc, &ahc->untagged_queues[i]);
+}
+
+void
+ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
+{
+ struct scb *scb;
+
+ if (ahc->untagged_queue_lock != 0)
+ return;
+
+ if ((scb = TAILQ_FIRST(queue)) != NULL
+ && (scb->flags & SCB_ACTIVE) == 0) {
+ scb->flags |= SCB_ACTIVE;
+ ahc_queue_scb(ahc, scb);
+ }
+}
+
+/************************* Interrupt Handling *********************************/
+void
+ahc_handle_brkadrint(struct ahc_softc *ahc)
+{
+ /*
+ * We upset the sequencer :-(
+ * Lookup the error message
+ */
+ int i;
+ int error;
+
+ error = ahc_inb(ahc, ERROR);
+ for (i = 0; error != 1 && i < num_errors; i++)
+ error >>= 1;
+ printf("%s: brkadrint, %s at seqaddr = 0x%x\n",
+ ahc_name(ahc), ahc_hard_errors[i].errmesg,
+ ahc_inb(ahc, SEQADDR0) |
+ (ahc_inb(ahc, SEQADDR1) << 8));
+
+ ahc_dump_card_state(ahc);
+
+ /* Tell everyone that this HBA is no longer availible */
+ ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+ CAM_NO_HBA);
+
+ /* Disable all interrupt sources by resetting the controller */
+ ahc_shutdown(ahc);
+}
+
+void
+ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
+{
+ struct scb *scb;
+ struct ahc_devinfo devinfo;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+
+ /*
+ * Clear the upper byte that holds SEQINT status
+ * codes and clear the SEQINT bit. We will unpause
+ * the sequencer, if appropriate, after servicing
+ * the request.
+ */
+ ahc_outb(ahc, CLRINT, CLRSEQINT);
+ switch (intstat & SEQINT_MASK) {
+ case BAD_STATUS:
+ {
+ u_int scb_index;
+ struct hardware_scb *hscb;
+
+ /*
+ * Set the default return value to 0 (don't
+ * send sense). The sense code will change
+ * this if needed.
+ */
+ ahc_outb(ahc, RETURN_1, 0);
+
+ /*
+ * The sequencer will notify us when a command
+ * has an error that would be of interest to
+ * the kernel. This allows us to leave the sequencer
+ * running in the common case of command completes
+ * without error. The sequencer will already have
+ * dma'd the SCB back up to us, so we can reference
+ * the in kernel copy directly.
+ */
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("%s:%c:%d: ahc_intr - referenced scb "
+ "not valid during seqint 0x%x scb(%d)\n",
+ ahc_name(ahc), devinfo.channel,
+ devinfo.target, intstat, scb_index);
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ goto unpause;
+ }
+
+ hscb = scb->hscb;
+
+ /* Don't want to clobber the original sense code */
+ if ((scb->flags & SCB_SENSE) != 0) {
+ /*
+ * Clear the SCB_SENSE Flag and have
+ * the sequencer do a normal command
+ * complete.
+ */
+ scb->flags &= ~SCB_SENSE;
+ ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ break;
+ }
+ ahc_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+ /* Freeze the queue until the client sees the error. */
+ ahc_freeze_devq(ahc, scb);
+ ahc_freeze_scb(scb);
+ ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status);
+ switch (hscb->shared_data.status.scsi_status) {
+ case SCSI_STATUS_OK:
+ printf("%s: Interrupted for staus of 0???\n",
+ ahc_name(ahc));
+ break;
+ case SCSI_STATUS_CMD_TERMINATED:
+ case SCSI_STATUS_CHECK_COND:
+ {
+ struct ahc_dma_seg *sg;
+ struct scsi_sense *sc;
+ struct ahc_initiator_tinfo *targ_info;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_transinfo *tinfo;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWSENSE) {
+ ahc_print_path(ahc, scb);
+ printf("SCB %d: requests Check Status\n",
+ scb->hscb->tag);
+ }
+#endif
+
+ if (ahc_perform_autosense(scb) == 0)
+ break;
+
+ targ_info = ahc_fetch_transinfo(ahc,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo = &targ_info->curr;
+ sg = scb->sg_list;
+ sc = (struct scsi_sense *)(&hscb->shared_data.cdb);
+ /*
+ * Save off the residual if there is one.
+ */
+ ahc_update_residual(ahc, scb);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWSENSE) {
+ ahc_print_path(ahc, scb);
+ printf("Sending Sense\n");
+ }
+#endif
+ sg->addr = ahc_get_sense_bufaddr(ahc, scb);
+ sg->len = ahc_get_sense_bufsize(ahc, scb);
+ sg->len |= AHC_DMA_LAST_SEG;
+
+ /* Fixup byte order */
+ sg->addr = ahc_htole32(sg->addr);
+ sg->len = ahc_htole32(sg->len);
+
+ sc->opcode = REQUEST_SENSE;
+ sc->byte2 = 0;
+ if (tinfo->protocol_version <= SCSI_REV_2
+ && SCB_GET_LUN(scb) < 8)
+ sc->byte2 = SCB_GET_LUN(scb) << 5;
+ sc->unused[0] = 0;
+ sc->unused[1] = 0;
+ sc->length = sg->len;
+ sc->control = 0;
+
+ /*
+ * We can't allow the target to disconnect.
+ * This will be an untagged transaction and
+ * having the target disconnect will make this
+ * transaction indestinguishable from outstanding
+ * tagged transactions.
+ */
+ hscb->control = 0;
+
+ /*
+ * This request sense could be because the
+ * the device lost power or in some other
+ * way has lost our transfer negotiations.
+ * Renegotiate if appropriate. Unit attention
+ * errors will be reported before any data
+ * phases occur.
+ */
+ if (ahc_get_residual(scb)
+ == ahc_get_transfer_length(scb)) {
+ ahc_update_neg_request(ahc, &devinfo,
+ tstate, targ_info,
+ /*force*/TRUE);
+ }
+ if (tstate->auto_negotiate & devinfo.target_mask) {
+ hscb->control |= MK_MESSAGE;
+ scb->flags &= ~SCB_NEGOTIATE;
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ }
+ hscb->cdb_len = sizeof(*sc);
+ hscb->dataptr = sg->addr;
+ hscb->datacnt = sg->len;
+ hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
+ hscb->sgptr = ahc_htole32(hscb->sgptr);
+ scb->sg_count = 1;
+ scb->flags |= SCB_SENSE;
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ ahc_outb(ahc, RETURN_1, SEND_SENSE);
+#ifdef __FreeBSD__
+ /*
+ * Ensure we have enough time to actually
+ * retrieve the sense.
+ */
+ untimeout(ahc_timeout, (caddr_t)scb,
+ scb->io_ctx->ccb_h.timeout_ch);
+ scb->io_ctx->ccb_h.timeout_ch =
+ timeout(ahc_timeout, (caddr_t)scb, 5 * hz);
+#endif
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case NO_MATCH:
+ {
+ /* Ensure we don't leave the selection hardware on */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+ printf("%s:%c:%d: no active SCB for reconnecting "
+ "target - issuing BUS DEVICE RESET\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target);
+ printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+ "ARG_1 == 0x%x ACCUM = 0x%x\n",
+ ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+ ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+ printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+ "SINDEX == 0x%x\n",
+ ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+ ahc_index_busy_tcl(ahc,
+ BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+ ahc_inb(ahc, SAVED_LUN))),
+ ahc_inb(ahc, SINDEX));
+ printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+ "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+ ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+ ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+ ahc_inb(ahc, SCB_CONTROL));
+ printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+ ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+ printf("SXFRCTL0 == 0x%x\n", ahc_inb(ahc, SXFRCTL0));
+ printf("SEQCTL == 0x%x\n", ahc_inb(ahc, SEQCTL));
+ ahc_dump_card_state(ahc);
+ ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
+ ahc->msgout_len = 1;
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_assert_atn(ahc);
+ break;
+ }
+ case SEND_REJECT:
+ {
+ u_int rejbyte = ahc_inb(ahc, ACCUM);
+ printf("%s:%c:%d: Warning - unknown message received from "
+ "target (0x%x). Rejecting\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
+ break;
+ }
+ case NO_IDENT:
+ {
+ /*
+ * The reconnecting target either did not send an identify
+ * message, or did, but we didn't find an SCB to match and
+ * before it could respond to our ATN/abort, it hit a dataphase.
+ * The only safe thing to do is to blow it away with a bus
+ * reset.
+ */
+ int found;
+
+ printf("%s:%c:%d: Target did not send an IDENTIFY message. "
+ "LASTPHASE = 0x%x, SAVED_SCSIID == 0x%x\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ ahc_inb(ahc, LASTPHASE), ahc_inb(ahc, SAVED_SCSIID));
+ found = ahc_reset_channel(ahc, devinfo.channel,
+ /*initiate reset*/TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahc_name(ahc), devinfo.channel,
+ found);
+ return;
+ }
+ case IGN_WIDE_RES:
+ ahc_handle_ign_wide_residue(ahc, &devinfo);
+ break;
+ case PDATA_REINIT:
+ ahc_reinitialize_dataptrs(ahc);
+ break;
+ case BAD_PHASE:
+ {
+ u_int lastphase;
+
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ printf("%s:%c:%d: unknown scsi bus phase %x, "
+ "lastphase = 0x%x. Attempting to continue\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ lastphase, ahc_inb(ahc, SCSISIGI));
+ break;
+ }
+ case MISSED_BUSFREE:
+ {
+ u_int lastphase;
+
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ printf("%s:%c:%d: Missed busfree. "
+ "Lastphase = 0x%x, Curphase = 0x%x\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ lastphase, ahc_inb(ahc, SCSISIGI));
+ ahc_restart(ahc);
+ return;
+ }
+ case HOST_MSG_LOOP:
+ {
+ /*
+ * The sequencer has encountered a message phase
+ * that requires host assistance for completion.
+ * While handling the message phase(s), we will be
+ * notified by the sequencer after each byte is
+ * transfered so we can track bus phase changes.
+ *
+ * If this is the first time we've seen a HOST_MSG_LOOP
+ * interrupt, initialize the state of the host message
+ * loop.
+ */
+ if (ahc->msg_type == MSG_TYPE_NONE) {
+ struct scb *scb;
+ u_int scb_index;
+ u_int bus_phase;
+
+ bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ if (bus_phase != P_MESGIN
+ && bus_phase != P_MESGOUT) {
+ printf("ahc_intr: HOST_MSG_LOOP bad "
+ "phase 0x%x\n",
+ bus_phase);
+ /*
+ * Probably transitioned to bus free before
+ * we got here. Just punt the message.
+ */
+ ahc_clear_intstat(ahc);
+ ahc_restart(ahc);
+ return;
+ }
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (devinfo.role == ROLE_INITIATOR) {
+ if (scb == NULL)
+ panic("HOST_MSG_LOOP with "
+ "invalid SCB %x\n", scb_index);
+
+ if (bus_phase == P_MESGOUT)
+ ahc_setup_initiator_msgout(ahc,
+ &devinfo,
+ scb);
+ else {
+ ahc->msg_type =
+ MSG_TYPE_INITIATOR_MSGIN;
+ ahc->msgin_index = 0;
+ }
+ }
+#if AHC_TARGET_MODE
+ else {
+ if (bus_phase == P_MESGOUT) {
+ ahc->msg_type =
+ MSG_TYPE_TARGET_MSGOUT;
+ ahc->msgin_index = 0;
+ }
+ else
+ ahc_setup_target_msgin(ahc,
+ &devinfo,
+ scb);
+ }
+#endif
+ }
+
+ ahc_handle_message_phase(ahc);
+ break;
+ }
+ case PERR_DETECTED:
+ {
+ /*
+ * If we've cleared the parity error interrupt
+ * but the sequencer still believes that SCSIPERR
+ * is true, it must be that the parity error is
+ * for the currently presented byte on the bus,
+ * and we are not in a phase (data-in) where we will
+ * eventually ack this byte. Ack the byte and
+ * throw it away in the hope that the target will
+ * take us to message out to deliver the appropriate
+ * error message.
+ */
+ if ((intstat & SCSIINT) == 0
+ && (ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0) {
+
+ if ((ahc->features & AHC_DT) == 0) {
+ u_int curphase;
+
+ /*
+ * The hardware will only let you ack bytes
+ * if the expected phase in SCSISIGO matches
+ * the current phase. Make sure this is
+ * currently the case.
+ */
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ ahc_outb(ahc, LASTPHASE, curphase);
+ ahc_outb(ahc, SCSISIGO, curphase);
+ }
+ ahc_inb(ahc, SCSIDATL);
+ }
+ break;
+ }
+ case DATA_OVERRUN:
+ {
+ /*
+ * When the sequencer detects an overrun, it
+ * places the controller in "BITBUCKET" mode
+ * and allows the target to complete its transfer.
+ * Unfortunately, none of the counters get updated
+ * when the controller is in this mode, so we have
+ * no way of knowing how large the overrun was.
+ */
+ u_int scbindex = ahc_inb(ahc, SCB_TAG);
+ u_int lastphase = ahc_inb(ahc, LASTPHASE);
+ u_int i;
+
+ scb = ahc_lookup_scb(ahc, scbindex);
+ for (i = 0; i < num_phases; i++) {
+ if (lastphase == ahc_phase_table[i].phase)
+ break;
+ }
+ ahc_print_path(ahc, scb);
+ printf("data overrun detected %s."
+ " Tag == 0x%x.\n",
+ ahc_phase_table[i].phasemsg,
+ scb->hscb->tag);
+ ahc_print_path(ahc, scb);
+ printf("%s seen Data Phase. Length = %ld. NumSGs = %d.\n",
+ ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+ ahc_get_transfer_length(scb), scb->sg_count);
+ if (scb->sg_count > 0) {
+ for (i = 0; i < scb->sg_count; i++) {
+
+ printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+ i,
+ (ahc_le32toh(scb->sg_list[i].len) >> 24
+ & SG_HIGH_ADDR_BITS),
+ ahc_le32toh(scb->sg_list[i].addr),
+ ahc_le32toh(scb->sg_list[i].len)
+ & AHC_SG_LEN_MASK);
+ }
+ }
+ /*
+ * Set this and it will take effect when the
+ * target does a command complete.
+ */
+ ahc_freeze_devq(ahc, scb);
+ if ((scb->flags & SCB_SENSE) == 0) {
+ ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ } else {
+ scb->flags &= ~SCB_SENSE;
+ ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ }
+ ahc_freeze_scb(scb);
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ /*
+ * Clear the channel in case we return
+ * to data phase later.
+ */
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+ }
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ u_int dscommand1;
+
+ /* Ensure HHADDR is 0 for future DMA operations. */
+ dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+ ahc_outb(ahc, HADDR, 0);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1);
+ }
+ break;
+ }
+ case MKMSG_FAILED:
+ {
+ u_int scbindex;
+
+ printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ devinfo.lun);
+ scbindex = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scbindex);
+ if (scb != NULL
+ && (scb->flags & SCB_RECOVERY_SCB) != 0)
+ /*
+ * Ensure that we didn't put a second instance of this
+ * SCB into the QINFIFO.
+ */
+ ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), scb->hscb->tag,
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_REMOVE);
+ break;
+ }
+ case NO_FREE_SCB:
+ {
+ printf("%s: No free or disconnected SCBs\n", ahc_name(ahc));
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ break;
+ }
+ case SCB_MISMATCH:
+ {
+ u_int scbptr;
+
+ scbptr = ahc_inb(ahc, SCBPTR);
+ printf("Bogus TAG after DMA. SCBPTR %d, tag %d, our tag %d\n",
+ scbptr, ahc_inb(ahc, ARG_1),
+ ahc->scb_data->hscbs[scbptr].tag);
+ ahc_dump_card_state(ahc);
+ panic("for saftey");
+ break;
+ }
+ case OUT_OF_RANGE:
+ {
+ printf("%s: BTT calculation out of range\n", ahc_name(ahc));
+ printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+ "ARG_1 == 0x%x ACCUM = 0x%x\n",
+ ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+ ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+ printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+ "SINDEX == 0x%x\n, A == 0x%x\n",
+ ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+ ahc_index_busy_tcl(ahc,
+ BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+ ahc_inb(ahc, SAVED_LUN))),
+ ahc_inb(ahc, SINDEX),
+ ahc_inb(ahc, ACCUM));
+ printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+ "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+ ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+ ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+ ahc_inb(ahc, SCB_CONTROL));
+ printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+ ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ break;
+ }
+ default:
+ printf("ahc_intr: seqint, "
+ "intstat == 0x%x, scsisigi = 0x%x\n",
+ intstat, ahc_inb(ahc, SCSISIGI));
+ break;
+ }
+unpause:
+ /*
+ * The sequencer is paused immediately on
+ * a SEQINT, so we should restart it when
+ * we're done.
+ */
+ ahc_unpause(ahc);
+}
+
+void
+ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
+{
+ u_int scb_index;
+ u_int status0;
+ u_int status;
+ struct scb *scb;
+ char cur_channel;
+ char intr_channel;
+
+ /* Make sure the sequencer is in a safe location. */
+ ahc_clear_critical_section(ahc);
+
+ if ((ahc->features & AHC_TWIN) != 0
+ && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
+ cur_channel = 'B';
+ else
+ cur_channel = 'A';
+ intr_channel = cur_channel;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ status0 = ahc_inb(ahc, SSTAT0) & IOERR;
+ else
+ status0 = 0;
+ status = ahc_inb(ahc, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+ if (status == 0 && status0 == 0) {
+ if ((ahc->features & AHC_TWIN) != 0) {
+ /* Try the other channel */
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+ status = ahc_inb(ahc, SSTAT1)
+ & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+ intr_channel = (cur_channel == 'A') ? 'B' : 'A';
+ }
+ if (status == 0) {
+ printf("%s: Spurious SCSI interrupt\n", ahc_name(ahc));
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_unpause(ahc);
+ return;
+ }
+ }
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb != NULL
+ && (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) == 0)
+ scb = NULL;
+
+ if ((ahc->features & AHC_ULTRA2) != 0
+ && (status0 & IOERR) != 0) {
+ int now_lvd;
+
+ now_lvd = ahc_inb(ahc, SBLKCTL) & ENAB40;
+ printf("%s: Transceiver State Has Changed to %s mode\n",
+ ahc_name(ahc), now_lvd ? "LVD" : "SE");
+ ahc_outb(ahc, CLRSINT0, CLRIOERR);
+ /*
+ * When transitioning to SE mode, the reset line
+ * glitches, triggering an arbitration bug in some
+ * Ultra2 controllers. This bug is cleared when we
+ * assert the reset line. Since a reset glitch has
+ * already occurred with this transition and a
+ * transceiver state change is handled just like
+ * a bus reset anyway, asserting the reset line
+ * ourselves is safe.
+ */
+ ahc_reset_channel(ahc, intr_channel,
+ /*Initiate Reset*/now_lvd == 0);
+ } else if ((status & SCSIRSTI) != 0) {
+ printf("%s: Someone reset channel %c\n",
+ ahc_name(ahc), intr_channel);
+ if (intr_channel != cur_channel)
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+ ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
+ } else if ((status & SCSIPERR) != 0) {
+ /*
+ * Determine the bus phase and queue an appropriate message.
+ * SCSIPERR is latched true as soon as a parity error
+ * occurs. If the sequencer acked the transfer that
+ * caused the parity error and the currently presented
+ * transfer on the bus has correct parity, SCSIPERR will
+ * be cleared by CLRSCSIPERR. Use this to determine if
+ * we should look at the last phase the sequencer recorded,
+ * or the current phase presented on the bus.
+ */
+ u_int mesg_out;
+ u_int curphase;
+ u_int errorphase;
+ u_int lastphase;
+ u_int scsirate;
+ u_int i;
+ u_int sstat2;
+
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ sstat2 = ahc_inb(ahc, SSTAT2);
+ ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
+ /*
+ * For all phases save DATA, the sequencer won't
+ * automatically ack a byte that has a parity error
+ * in it. So the only way that the current phase
+ * could be 'data-in' is if the parity error is for
+ * an already acked byte in the data phase. During
+ * synchronous data-in transfers, we may actually
+ * ack bytes before latching the current phase in
+ * LASTPHASE, leading to the discrepancy between
+ * curphase and lastphase.
+ */
+ if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
+ || curphase == P_DATAIN || curphase == P_DATAIN_DT)
+ errorphase = curphase;
+ else
+ errorphase = lastphase;
+
+ for (i = 0; i < num_phases; i++) {
+ if (errorphase == ahc_phase_table[i].phase)
+ break;
+ }
+ mesg_out = ahc_phase_table[i].mesg_out;
+ if (scb != NULL)
+ ahc_print_path(ahc, scb);
+ else
+ printf("%s:%c:%d: ", ahc_name(ahc), intr_channel,
+ SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
+ scsirate = ahc_inb(ahc, SCSIRATE);
+ printf("parity error detected %s. "
+ "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
+ ahc_phase_table[i].phasemsg,
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8),
+ scsirate);
+
+ if ((ahc->features & AHC_DT) != 0) {
+
+ if ((sstat2 & CRCVALERR) != 0)
+ printf("\tCRC Value Mismatch\n");
+ if ((sstat2 & CRCENDERR) != 0)
+ printf("\tNo terminal CRC packet recevied\n");
+ if ((sstat2 & CRCREQERR) != 0)
+ printf("\tIllegal CRC packet request\n");
+ if ((sstat2 & DUAL_EDGE_ERR) != 0)
+ printf("\tUnexpected %sDT Data Phase\n",
+ (scsirate & SINGLE_EDGE) ? "" : "non-");
+ }
+
+ /*
+ * We've set the hardware to assert ATN if we
+ * get a parity error on "in" phases, so all we
+ * need to do is stuff the message buffer with
+ * the appropriate message. "In" phases have set
+ * mesg_out to something other than MSG_NOP.
+ */
+ if (mesg_out != MSG_NOOP) {
+ if (ahc->msg_type != MSG_TYPE_NONE)
+ ahc->send_msg_perror = TRUE;
+ else
+ ahc_outb(ahc, MSG_OUT, mesg_out);
+ }
+ /*
+ * Force a renegotiation with this target just in
+ * case we are out of sync for some external reason
+ * unknown (or unreported) by the target.
+ */
+ ahc_force_renegotiation(ahc);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_unpause(ahc);
+ } else if ((status & SELTO) != 0) {
+ u_int scbptr;
+
+ /* Stop the selection */
+ ahc_outb(ahc, SCSISEQ, 0);
+
+ /* No more pending messages */
+ ahc_clear_msg_state(ahc);
+
+ /* Clear interrupt state */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+ ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+ /*
+ * Although the driver does not care about the
+ * 'Selection in Progress' status bit, the busy
+ * LED does. SELINGO is only cleared by a sucessfull
+ * selection, so we must manually clear it to insure
+ * the LED turns off just incase no future successful
+ * selections occur (e.g. no devices on the bus).
+ */
+ ahc_outb(ahc, CLRSINT0, CLRSELINGO);
+
+ scbptr = ahc_inb(ahc, WAITING_SCBH);
+ ahc_outb(ahc, SCBPTR, scbptr);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("%s: ahc_intr - referenced scb not "
+ "valid during SELTO scb(%d, %d)\n",
+ ahc_name(ahc), scbptr, scb_index);
+ } else {
+ ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+ ahc_freeze_devq(ahc, scb);
+ }
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ /*
+ * Force a renegotiation with this target just in
+ * case the cable was pulled and will later be
+ * re-attached. The target may forget its negotiation
+ * settings with us should it attempt to reselect
+ * during the interruption. The target will not issue
+ * a unit attention in this case, so we must always
+ * renegotiate.
+ */
+ ahc_force_renegotiation(ahc);
+ ahc_restart(ahc);
+ } else if ((status & BUSFREE) != 0
+ && (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
+ u_int lastphase;
+ u_int saved_scsiid;
+ u_int saved_lun;
+ u_int target;
+ u_int initiator_role_id;
+ char channel;
+ int printerror;
+
+ /*
+ * Clear our selection hardware as soon as possible.
+ * We may have an entry in the waiting Q for this target,
+ * that is affected by this busfree and we don't want to
+ * go about selecting the target while we handle the event.
+ */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+ /*
+ * Disable busfree interrupts and clear the busfree
+ * interrupt status. We do this here so that several
+ * bus transactions occur prior to clearing the SCSIINT
+ * latch. It can take a bit for the clearing to take effect.
+ */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+ ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
+
+ /*
+ * Look at what phase we were last in.
+ * If its message out, chances are pretty good
+ * that the busfree was in response to one of
+ * our abort requests.
+ */
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+ saved_lun = ahc_inb(ahc, SAVED_LUN);
+ target = SCSIID_TARGET(ahc, saved_scsiid);
+ initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+ channel = SCSIID_CHANNEL(ahc, saved_scsiid);
+ printerror = 1;
+
+ if (lastphase == P_MESGOUT) {
+ struct ahc_devinfo devinfo;
+ u_int tag;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+ tag = SCB_LIST_NULL;
+ if (ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT_TAG, TRUE)
+ || ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT, TRUE)) {
+ if (ahc->msgout_buf[ahc->msgout_index - 1]
+ == MSG_ABORT_TAG)
+ tag = scb->hscb->tag;
+ ahc_print_path(ahc, scb);
+ printf("SCB %d - Abort%s Completed.\n",
+ scb->hscb->tag, tag == SCB_LIST_NULL ?
+ "" : " Tag");
+ ahc_abort_scbs(ahc, target, channel,
+ saved_lun, tag,
+ ROLE_INITIATOR,
+ CAM_REQ_ABORTED);
+ printerror = 0;
+ } else if (ahc_sent_msg(ahc, AHCMSG_1B,
+ MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+ /*
+ * Don't mark the user's request for this BDR
+ * as completing with CAM_BDR_SENT. CAM3
+ * specifies CAM_REQ_CMP.
+ */
+ if (scb != NULL
+ && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+ && ahc_match_scb(ahc, scb, target, channel,
+ CAM_LUN_WILDCARD,
+ SCB_LIST_NULL,
+ ROLE_INITIATOR)) {
+ ahc_set_transaction_status(scb, CAM_REQ_CMP);
+ }
+#endif
+ ahc_compile_devinfo(&devinfo,
+ initiator_role_id,
+ target,
+ CAM_LUN_WILDCARD,
+ channel,
+ ROLE_INITIATOR);
+ ahc_handle_devreset(ahc, &devinfo,
+ CAM_BDR_SENT,
+ "Bus Device Reset",
+ /*verbose_level*/0);
+ printerror = 0;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+ MSG_EXT_PPR, FALSE)) {
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+
+ /*
+ * PPR Rejected. Try non-ppr negotiation
+ * and retry command.
+ */
+ tinfo = ahc_fetch_transinfo(ahc,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->goal.ppr_options = 0;
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ printerror = 0;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+ MSG_EXT_WDTR, FALSE)
+ || ahc_sent_msg(ahc, AHCMSG_EXT,
+ MSG_EXT_SDTR, FALSE)) {
+ /*
+ * Negotiation Rejected. Go-async and
+ * retry command.
+ */
+ ahc_set_width(ahc, &devinfo,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahc_set_syncrate(ahc, &devinfo,
+ /*syncrate*/NULL,
+ /*period*/0, /*offset*/0,
+ /*ppr_options*/0,
+ AHC_TRANS_CUR|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ printerror = 0;
+ }
+ }
+ if (printerror != 0) {
+ u_int i;
+
+ if (scb != NULL) {
+ u_int tag;
+
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ tag = scb->hscb->tag;
+ else
+ tag = SCB_LIST_NULL;
+ ahc_print_path(ahc, scb);
+ ahc_abort_scbs(ahc, target, channel,
+ SCB_GET_LUN(scb), tag,
+ ROLE_INITIATOR,
+ CAM_UNEXP_BUSFREE);
+ } else {
+ /*
+ * We had not fully identified this connection,
+ * so we cannot abort anything.
+ */
+ printf("%s: ", ahc_name(ahc));
+ }
+ for (i = 0; i < num_phases; i++) {
+ if (lastphase == ahc_phase_table[i].phase)
+ break;
+ }
+ /*
+ * Renegotiate with this device at the
+ * next oportunity just in case this busfree
+ * is due to a negotiation mismatch with the
+ * device.
+ */
+ ahc_force_renegotiation(ahc);
+ printf("Unexpected busfree %s\n"
+ "SEQADDR == 0x%x\n",
+ ahc_phase_table[i].phasemsg,
+ ahc_inb(ahc, SEQADDR0)
+ | (ahc_inb(ahc, SEQADDR1) << 8));
+ }
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_restart(ahc);
+ } else {
+ printf("%s: Missing case in ahc_handle_scsiint. status = %x\n",
+ ahc_name(ahc), status);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ }
+}
+
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahc_force_renegotiation(struct ahc_softc *ahc)
+{
+ struct ahc_devinfo devinfo;
+ struct ahc_initiator_tinfo *targ_info;
+ struct ahc_tmode_tstate *tstate;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+ targ_info = ahc_fetch_transinfo(ahc,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ ahc_update_neg_request(ahc, &devinfo, tstate,
+ targ_info, /*force*/TRUE);
+}
+
+#define AHC_MAX_STEPS 2000
+void
+ahc_clear_critical_section(struct ahc_softc *ahc)
+{
+ int stepping;
+ int steps;
+ u_int simode0;
+ u_int simode1;
+
+ if (ahc->num_critical_sections == 0)
+ return;
+
+ stepping = FALSE;
+ steps = 0;
+ simode0 = 0;
+ simode1 = 0;
+ for (;;) {
+ struct cs *cs;
+ u_int seqaddr;
+ u_int i;
+
+ seqaddr = ahc_inb(ahc, SEQADDR0)
+ | (ahc_inb(ahc, SEQADDR1) << 8);
+
+ /*
+ * Seqaddr represents the next instruction to execute,
+ * so we are really executing the instruction just
+ * before it.
+ */
+ if (seqaddr != 0)
+ seqaddr -= 1;
+ cs = ahc->critical_sections;
+ for (i = 0; i < ahc->num_critical_sections; i++, cs++) {
+
+ if (cs->begin < seqaddr && cs->end >= seqaddr)
+ break;
+ }
+
+ if (i == ahc->num_critical_sections)
+ break;
+
+ if (steps > AHC_MAX_STEPS) {
+ printf("%s: Infinite loop in critical section\n",
+ ahc_name(ahc));
+ ahc_dump_card_state(ahc);
+ panic("critical section loop");
+ }
+
+ steps++;
+ if (stepping == FALSE) {
+
+ /*
+ * Disable all interrupt sources so that the
+ * sequencer will not be stuck by a pausing
+ * interrupt condition while we attempt to
+ * leave a critical section.
+ */
+ simode0 = ahc_inb(ahc, SIMODE0);
+ ahc_outb(ahc, SIMODE0, 0);
+ simode1 = ahc_inb(ahc, SIMODE1);
+ ahc_outb(ahc, SIMODE1, 0);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP);
+ stepping = TRUE;
+ }
+ ahc_outb(ahc, HCNTRL, ahc->unpause);
+ while (!ahc_is_paused(ahc))
+ ahc_delay(200);
+ }
+ if (stepping) {
+ ahc_outb(ahc, SIMODE0, simode0);
+ ahc_outb(ahc, SIMODE1, simode1);
+ ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) & ~STEP);
+ }
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahc_clear_intstat(struct ahc_softc *ahc)
+{
+ /* Clear any interrupt conditions this may have caused */
+ ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+ |CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
+ CLRREQINIT);
+ ahc_flush_device_writes(ahc);
+ ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
+ ahc_flush_device_writes(ahc);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_flush_device_writes(ahc);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHC_DEBUG
+int ahc_debug = AHC_DEBUG;
+#endif
+
+void
+ahc_print_scb(struct scb *scb)
+{
+ int i;
+
+ struct hardware_scb *hscb = scb->hscb;
+
+ printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+ (void *)scb,
+ hscb->control,
+ hscb->scsiid,
+ hscb->lun,
+ hscb->cdb_len);
+ printf("Shared Data: ");
+ for (i = 0; i < sizeof(hscb->shared_data.cdb); i++)
+ printf("%#02x", hscb->shared_data.cdb[i]);
+ printf(" dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
+ ahc_le32toh(hscb->dataptr),
+ ahc_le32toh(hscb->datacnt),
+ ahc_le32toh(hscb->sgptr),
+ hscb->tag);
+ if (scb->sg_count > 0) {
+ for (i = 0; i < scb->sg_count; i++) {
+ printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+ i,
+ (ahc_le32toh(scb->sg_list[i].len) >> 24
+ & SG_HIGH_ADDR_BITS),
+ ahc_le32toh(scb->sg_list[i].addr),
+ ahc_le32toh(scb->sg_list[i].len));
+ }
+ }
+}
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahc_tmode_tstate *
+ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
+{
+ struct ahc_tmode_tstate *master_tstate;
+ struct ahc_tmode_tstate *tstate;
+ int i;
+
+ master_tstate = ahc->enabled_targets[ahc->our_id];
+ if (channel == 'B') {
+ scsi_id += 8;
+ master_tstate = ahc->enabled_targets[ahc->our_id_b + 8];
+ }
+ if (ahc->enabled_targets[scsi_id] != NULL
+ && ahc->enabled_targets[scsi_id] != master_tstate)
+ panic("%s: ahc_alloc_tstate - Target already allocated",
+ ahc_name(ahc));
+ tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
+ if (tstate == NULL)
+ return (NULL);
+
+ /*
+ * If we have allocated a master tstate, copy user settings from
+ * the master tstate (taken from SRAM or the EEPROM) for this
+ * channel, but reset our current and goal settings to async/narrow
+ * until an initiator talks to us.
+ */
+ if (master_tstate != NULL) {
+ memcpy(tstate, master_tstate, sizeof(*tstate));
+ memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+ tstate->ultraenb = 0;
+ for (i = 0; i < AHC_NUM_TARGETS; i++) {
+ memset(&tstate->transinfo[i].curr, 0,
+ sizeof(tstate->transinfo[i].curr));
+ memset(&tstate->transinfo[i].goal, 0,
+ sizeof(tstate->transinfo[i].goal));
+ }
+ } else
+ memset(tstate, 0, sizeof(*tstate));
+ ahc->enabled_targets[scsi_id] = tstate;
+ return (tstate);
+}
+
+#ifdef AHC_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
+{
+ struct ahc_tmode_tstate *tstate;
+
+ /*
+ * Don't clean up our "master" tstate.
+ * It has our default user settings.
+ */
+ if (((channel == 'B' && scsi_id == ahc->our_id_b)
+ || (channel == 'A' && scsi_id == ahc->our_id))
+ && force == FALSE)
+ return;
+
+ if (channel == 'B')
+ scsi_id += 8;
+ tstate = ahc->enabled_targets[scsi_id];
+ if (tstate != NULL)
+ free(tstate, M_DEVBUF);
+ ahc->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest syncrate to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+struct ahc_syncrate *
+ahc_devlimited_syncrate(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ u_int *period, u_int *ppr_options, role_t role)
+{
+ struct ahc_transinfo *transinfo;
+ u_int maxsync;
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
+ && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
+ maxsync = AHC_SYNCRATE_DT;
+ } else {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ /* Can't do DT on an SE bus */
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ } else if ((ahc->features & AHC_ULTRA) != 0) {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ } else {
+ maxsync = AHC_SYNCRATE_FAST;
+ }
+ /*
+ * Never allow a value higher than our current goal
+ * period otherwise we may allow a target initiated
+ * negotiation to go above the limit as set by the
+ * user. In the case of an initiator initiated
+ * sync negotiation, we limit based on the user
+ * setting. This allows the system to still accept
+ * incoming negotiations even if target initiated
+ * negotiation is not performed.
+ */
+ if (role == ROLE_TARGET)
+ transinfo = &tinfo->user;
+ else
+ transinfo = &tinfo->goal;
+ *ppr_options &= transinfo->ppr_options;
+ if (transinfo->period == 0) {
+ *period = 0;
+ *ppr_options = 0;
+ return (NULL);
+ }
+ *period = MAX(*period, transinfo->period);
+ return (ahc_find_syncrate(ahc, period, ppr_options, maxsync));
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+struct ahc_syncrate *
+ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+ u_int *ppr_options, u_int maxsync)
+{
+ struct ahc_syncrate *syncrate;
+
+ if ((ahc->features & AHC_DT) == 0)
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+ /* Skip all DT only entries if DT is not available */
+ if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && maxsync < AHC_SYNCRATE_ULTRA2)
+ maxsync = AHC_SYNCRATE_ULTRA2;
+
+ for (syncrate = &ahc_syncrates[maxsync];
+ syncrate->rate != NULL;
+ syncrate++) {
+
+ /*
+ * The Ultra2 table doesn't go as low
+ * as for the Fast/Ultra cards.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0
+ && (syncrate->sxfr_u2 == 0))
+ break;
+
+ if (*period <= syncrate->period) {
+ /*
+ * When responding to a target that requests
+ * sync, the requested rate may fall between
+ * two rates that we can output, but still be
+ * a rate that we can receive. Because of this,
+ * we want to respond to the target with
+ * the same rate that it sent to us even
+ * if the period we use to send data to it
+ * is lower. Only lower the response period
+ * if we must.
+ */
+ if (syncrate == &ahc_syncrates[maxsync])
+ *period = syncrate->period;
+
+ /*
+ * At some speeds, we only support
+ * ST transfers.
+ */
+ if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ break;
+ }
+ }
+
+ if ((*period == 0)
+ || (syncrate->rate == NULL)
+ || ((ahc->features & AHC_ULTRA2) != 0
+ && (syncrate->sxfr_u2 == 0))) {
+ /* Use asynchronous transfers. */
+ *period = 0;
+ syncrate = NULL;
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ return (syncrate);
+}
+
+/*
+ * Convert from an entry in our syncrate table to the SCSI equivalent
+ * sync "period" factor.
+ */
+u_int
+ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
+{
+ struct ahc_syncrate *syncrate;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ scsirate &= SXFR_ULTRA2;
+ else
+ scsirate &= SXFR;
+
+ syncrate = &ahc_syncrates[maxsync];
+ while (syncrate->rate != NULL) {
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ if (syncrate->sxfr_u2 == 0)
+ break;
+ else if (scsirate == (syncrate->sxfr_u2 & SXFR_ULTRA2))
+ return (syncrate->period);
+ } else if (scsirate == (syncrate->sxfr & SXFR)) {
+ return (syncrate->period);
+ }
+ syncrate++;
+ }
+ return (0); /* async */
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+void
+ahc_validate_offset(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ struct ahc_syncrate *syncrate,
+ u_int *offset, int wide, role_t role)
+{
+ u_int maxoffset;
+
+ /* Limit offset to what we can do */
+ if (syncrate == NULL) {
+ maxoffset = 0;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ maxoffset = MAX_OFFSET_ULTRA2;
+ } else {
+ if (wide)
+ maxoffset = MAX_OFFSET_16BIT;
+ else
+ maxoffset = MAX_OFFSET_8BIT;
+ }
+ *offset = MIN(*offset, maxoffset);
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *offset = MIN(*offset, tinfo->user.offset);
+ else
+ *offset = MIN(*offset, tinfo->goal.offset);
+ }
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+void
+ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
+ u_int *bus_width, role_t role)
+{
+ switch (*bus_width) {
+ default:
+ if (ahc->features & AHC_WIDE) {
+ /* Respond Wide */
+ *bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ break;
+ }
+ /* FALLTHROUGH */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ *bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *bus_width = MIN(tinfo->user.width, *bus_width);
+ else
+ *bus_width = MIN(tinfo->goal.width, *bus_width);
+ }
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient oportunity. This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahc_update_neg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct ahc_tmode_tstate *tstate,
+ struct ahc_initiator_tinfo *tinfo, int force)
+{
+ u_int auto_negotiate_orig;
+
+ auto_negotiate_orig = tstate->auto_negotiate;
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+ || (force
+ && (tinfo->goal.period != 0
+ || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+ || tinfo->goal.ppr_options != 0)))
+ tstate->auto_negotiate |= devinfo->target_mask;
+ else
+ tstate->auto_negotiate &= ~devinfo->target_mask;
+
+ return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct ahc_syncrate *syncrate, u_int period,
+ u_int offset, u_int ppr_options, u_int type, int paused)
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int old_period;
+ u_int old_offset;
+ u_int old_ppr;
+ int active;
+ int update_needed;
+
+ active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+ update_needed = 0;
+
+ if (syncrate == NULL) {
+ period = 0;
+ offset = 0;
+ }
+
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if ((type & AHC_TRANS_USER) != 0) {
+ tinfo->user.period = period;
+ tinfo->user.offset = offset;
+ tinfo->user.ppr_options = ppr_options;
+ }
+
+ if ((type & AHC_TRANS_GOAL) != 0) {
+ tinfo->goal.period = period;
+ tinfo->goal.offset = offset;
+ tinfo->goal.ppr_options = ppr_options;
+ }
+
+ old_period = tinfo->curr.period;
+ old_offset = tinfo->curr.offset;
+ old_ppr = tinfo->curr.ppr_options;
+
+ if ((type & AHC_TRANS_CUR) != 0
+ && (old_period != period
+ || old_offset != offset
+ || old_ppr != ppr_options)) {
+ u_int scsirate;
+
+ update_needed++;
+ scsirate = tinfo->scsirate;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+
+ scsirate &= ~(SXFR_ULTRA2|SINGLE_EDGE|ENABLE_CRC);
+ if (syncrate != NULL) {
+ scsirate |= syncrate->sxfr_u2;
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0)
+ scsirate |= ENABLE_CRC;
+ else
+ scsirate |= SINGLE_EDGE;
+ }
+ } else {
+
+ scsirate &= ~(SXFR|SOFS);
+ /*
+ * Ensure Ultra mode is set properly for
+ * this target.
+ */
+ tstate->ultraenb &= ~devinfo->target_mask;
+ if (syncrate != NULL) {
+ if (syncrate->sxfr & ULTRA_SXFR) {
+ tstate->ultraenb |=
+ devinfo->target_mask;
+ }
+ scsirate |= syncrate->sxfr & SXFR;
+ scsirate |= offset & SOFS;
+ }
+ if (active) {
+ u_int sxfrctl0;
+
+ sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+ sxfrctl0 &= ~FAST20;
+ if (tstate->ultraenb & devinfo->target_mask)
+ sxfrctl0 |= FAST20;
+ ahc_outb(ahc, SXFRCTL0, sxfrctl0);
+ }
+ }
+ if (active) {
+ ahc_outb(ahc, SCSIRATE, scsirate);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIOFFSET, offset);
+ }
+
+ tinfo->scsirate = scsirate;
+ tinfo->curr.period = period;
+ tinfo->curr.offset = offset;
+ tinfo->curr.ppr_options = ppr_options;
+
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ if (offset != 0) {
+ printf("%s: target %d synchronous at %sMHz%s, "
+ "offset = 0x%x\n", ahc_name(ahc),
+ devinfo->target, syncrate->rate,
+ (ppr_options & MSG_EXT_PPR_DT_REQ)
+ ? " DT" : "", offset);
+ } else {
+ printf("%s: target %d using "
+ "asynchronous transfers\n",
+ ahc_name(ahc), devinfo->target);
+ }
+ }
+ }
+
+ update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+ tinfo, /*force*/FALSE);
+
+ if (update_needed)
+ ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int width, u_int type, int paused)
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int oldwidth;
+ int active;
+ int update_needed;
+
+ active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+ update_needed = 0;
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if ((type & AHC_TRANS_USER) != 0)
+ tinfo->user.width = width;
+
+ if ((type & AHC_TRANS_GOAL) != 0)
+ tinfo->goal.width = width;
+
+ oldwidth = tinfo->curr.width;
+ if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
+ u_int scsirate;
+
+ update_needed++;
+ scsirate = tinfo->scsirate;
+ scsirate &= ~WIDEXFER;
+ if (width == MSG_EXT_WDTR_BUS_16_BIT)
+ scsirate |= WIDEXFER;
+
+ tinfo->scsirate = scsirate;
+
+ if (active)
+ ahc_outb(ahc, SCSIRATE, scsirate);
+
+ tinfo->curr.width = width;
+
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ printf("%s: target %d using %dbit transfers\n",
+ ahc_name(ahc), devinfo->target,
+ 8 * (0x01 << width));
+ }
+ }
+
+ update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+ tinfo, /*force*/FALSE);
+ if (update_needed)
+ ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+void
+ahc_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ ahc_queue_alg alg)
+{
+ ahc_platform_set_tags(ahc, devinfo, alg);
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ devinfo->lun, AC_TRANSFER_NEG, &alg);
+}
+
+/*
+ * When the transfer settings for a connection change, update any
+ * in-transit SCBs to contain the new data so the hardware will
+ * be set correctly during future (re)selections.
+ */
+static void
+ahc_update_pending_scbs(struct ahc_softc *ahc)
+{
+ struct scb *pending_scb;
+ int pending_scb_count;
+ int i;
+ int paused;
+ u_int saved_scbptr;
+
+ /*
+ * Traverse the pending SCB list and ensure that all of the
+ * SCBs there have the proper settings.
+ */
+ pending_scb_count = 0;
+ LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+ struct ahc_devinfo devinfo;
+ struct hardware_scb *pending_hscb;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+
+ ahc_scb_devinfo(ahc, &devinfo, pending_scb);
+ tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ pending_hscb = pending_scb->hscb;
+ pending_hscb->control &= ~ULTRAENB;
+ if ((tstate->ultraenb & devinfo.target_mask) != 0)
+ pending_hscb->control |= ULTRAENB;
+ pending_hscb->scsirate = tinfo->scsirate;
+ pending_hscb->scsioffset = tinfo->curr.offset;
+ if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+ && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+ pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+ pending_hscb->control &= ~MK_MESSAGE;
+ }
+ ahc_sync_scb(ahc, pending_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ pending_scb_count++;
+ }
+
+ if (pending_scb_count == 0)
+ return;
+
+ if (ahc_is_paused(ahc)) {
+ paused = 1;
+ } else {
+ paused = 0;
+ ahc_pause(ahc);
+ }
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ /* Ensure that the hscbs down on the card match the new information */
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ struct hardware_scb *pending_hscb;
+ u_int control;
+ u_int scb_tag;
+
+ ahc_outb(ahc, SCBPTR, i);
+ scb_tag = ahc_inb(ahc, SCB_TAG);
+ pending_scb = ahc_lookup_scb(ahc, scb_tag);
+ if (pending_scb == NULL)
+ continue;
+
+ pending_hscb = pending_scb->hscb;
+ control = ahc_inb(ahc, SCB_CONTROL);
+ control &= ~(ULTRAENB|MK_MESSAGE);
+ control |= pending_hscb->control & (ULTRAENB|MK_MESSAGE);
+ ahc_outb(ahc, SCB_CONTROL, control);
+ ahc_outb(ahc, SCB_SCSIRATE, pending_hscb->scsirate);
+ ahc_outb(ahc, SCB_SCSIOFFSET, pending_hscb->scsioffset);
+ }
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+
+ if (paused == 0)
+ ahc_unpause(ahc);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ u_int saved_scsiid;
+ role_t role;
+ int our_id;
+
+ if (ahc_inb(ahc, SSTAT0) & TARGET)
+ role = ROLE_TARGET;
+ else
+ role = ROLE_INITIATOR;
+
+ if (role == ROLE_TARGET
+ && (ahc->features & AHC_MULTI_TID) != 0
+ && (ahc_inb(ahc, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
+ /* We were selected, so pull our id from TARGIDIN */
+ our_id = ahc_inb(ahc, TARGIDIN) & OID;
+ } else if ((ahc->features & AHC_ULTRA2) != 0)
+ our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+ else
+ our_id = ahc_inb(ahc, SCSIID) & OID;
+
+ saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+ ahc_compile_devinfo(devinfo,
+ our_id,
+ SCSIID_TARGET(ahc, saved_scsiid),
+ ahc_inb(ahc, SAVED_LUN),
+ SCSIID_CHANNEL(ahc, saved_scsiid),
+ role);
+}
+
+struct ahc_phase_table_entry*
+ahc_lookup_phase_entry(int phase)
+{
+ struct ahc_phase_table_entry *entry;
+ struct ahc_phase_table_entry *last_entry;
+
+ /*
+ * num_phases doesn't include the default entry which
+ * will be returned if the phase doesn't match.
+ */
+ last_entry = &ahc_phase_table[num_phases];
+ for (entry = ahc_phase_table; entry < last_entry; entry++) {
+ if (phase == entry->phase)
+ break;
+ }
+ return (entry);
+}
+
+void
+ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
+ u_int lun, char channel, role_t role)
+{
+ devinfo->our_scsiid = our_id;
+ devinfo->target = target;
+ devinfo->lun = lun;
+ devinfo->target_offset = target;
+ devinfo->channel = channel;
+ devinfo->role = role;
+ if (channel == 'B')
+ devinfo->target_offset += 8;
+ devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+static void
+ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct scb *scb)
+{
+ role_t role;
+ int our_id;
+
+ our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+ role = ROLE_INITIATOR;
+ if ((scb->hscb->control & TARGET_SCB) != 0)
+ role = ROLE_TARGET;
+ ahc_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahc, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+static void
+ahc_assert_atn(struct ahc_softc *ahc)
+{
+ u_int scsisigo;
+
+ scsisigo = ATNO;
+ if ((ahc->features & AHC_DT) == 0)
+ scsisigo |= ahc_inb(ahc, SCSISIGI);
+ ahc_outb(ahc, SCSISIGO, scsisigo);
+}
+
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted. Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct scb *scb)
+{
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+
+ if ((scb->flags & SCB_DEVICE_RESET) == 0
+ && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) {
+ u_int identify_msg;
+
+ identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+ if ((scb->hscb->control & DISCENB) != 0)
+ identify_msg |= MSG_IDENTIFY_DISCFLAG;
+ ahc->msgout_buf[ahc->msgout_index++] = identify_msg;
+ ahc->msgout_len++;
+
+ if ((scb->hscb->control & TAG_ENB) != 0) {
+ ahc->msgout_buf[ahc->msgout_index++] =
+ scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+ ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag;
+ ahc->msgout_len += 2;
+ }
+ }
+
+ if (scb->flags & SCB_DEVICE_RESET) {
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
+ ahc->msgout_len++;
+ ahc_print_path(ahc, scb);
+ printf("Bus Device Reset Message Sent\n");
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else if ((scb->flags & SCB_ABORT) != 0) {
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG;
+ else
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT;
+ ahc->msgout_len++;
+ ahc_print_path(ahc, scb);
+ printf("Abort%s Message Sent\n",
+ (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
+ ahc_build_transfer_msg(ahc, devinfo);
+ } else {
+ printf("ahc_intr: AWAITING_MSG for an SCB that "
+ "does not have a waiting message\n");
+ printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+ devinfo->target_mask);
+ panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
+ "SCB flags = %x", scb->hscb->tag, scb->hscb->control,
+ ahc_inb(ahc, MSG_OUT), scb->flags);
+ }
+
+ /*
+ * Clear the MK_MESSAGE flag from the SCB so we aren't
+ * asked to send this message again.
+ */
+ ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE);
+ scb->hscb->control &= ~MK_MESSAGE;
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ /*
+ * We need to initiate transfer negotiations.
+ * If our current and goal settings are identical,
+ * we want to renegotiate due to a check condition.
+ */
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_syncrate *rate;
+ int dowide;
+ int dosync;
+ int doppr;
+ int use_ppr;
+ u_int period;
+ u_int ppr_options;
+ u_int offset;
+
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ /*
+ * Filter our period based on the current connection.
+ * If we can't perform DT transfers on this segment (not in LVD
+ * mode for instance), then our decision to issue a PPR message
+ * may change.
+ */
+ period = tinfo->goal.period;
+ ppr_options = tinfo->goal.ppr_options;
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ ppr_options = 0;
+ rate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+ &ppr_options, devinfo->role);
+ dowide = tinfo->curr.width != tinfo->goal.width;
+ dosync = tinfo->curr.period != period;
+ doppr = tinfo->curr.ppr_options != ppr_options;
+
+ if (!dowide && !dosync && !doppr) {
+ dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+ dosync = tinfo->goal.period != 0;
+ doppr = tinfo->goal.ppr_options != 0;
+ }
+
+ if (!dowide && !dosync && !doppr) {
+ panic("ahc_intr: AWAITING_MSG for negotiation, "
+ "but no negotiation needed\n");
+ }
+
+ use_ppr = (tinfo->curr.transport_version >= 3) || doppr;
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ use_ppr = 0;
+
+ /*
+ * Both the PPR message and SDTR message require the
+ * goal syncrate to be limited to what the target device
+ * is capable of handling (based on whether an LVD->SE
+ * expander is on the bus), so combine these two cases.
+ * Regardless, guarantee that if we are using WDTR and SDTR
+ * messages that WDTR comes first.
+ */
+ if (use_ppr || (dosync && !dowide)) {
+
+ offset = tinfo->goal.offset;
+ ahc_validate_offset(ahc, tinfo, rate, &offset,
+ use_ppr ? tinfo->goal.width
+ : tinfo->curr.width,
+ devinfo->role);
+ if (use_ppr) {
+ ahc_construct_ppr(ahc, devinfo, period, offset,
+ tinfo->goal.width, ppr_options);
+ } else {
+ ahc_construct_sdtr(ahc, devinfo, period, offset);
+ }
+ } else {
+ ahc_construct_wdtr(ahc, devinfo, tinfo->goal.width);
+ }
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int period, u_int offset)
+{
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR;
+ ahc->msgout_buf[ahc->msgout_index++] = period;
+ ahc->msgout_buf[ahc->msgout_index++] = offset;
+ ahc->msgout_len += 5;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ devinfo->lun, period, offset);
+ }
+}
+
+/*
+ * Build a wide negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int bus_width)
+{
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR;
+ ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+ ahc->msgout_len += 4;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending WDTR %x\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ devinfo->lun, bus_width);
+ }
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int period, u_int offset, u_int bus_width,
+ u_int ppr_options)
+{
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR_LEN;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR;
+ ahc->msgout_buf[ahc->msgout_index++] = period;
+ ahc->msgout_buf[ahc->msgout_index++] = 0;
+ ahc->msgout_buf[ahc->msgout_index++] = offset;
+ ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+ ahc->msgout_buf[ahc->msgout_index++] = ppr_options;
+ ahc->msgout_len += 8;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+ "offset %x, ppr_options %x\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target, devinfo->lun,
+ bus_width, period, offset, ppr_options);
+ }
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahc_clear_msg_state(struct ahc_softc *ahc)
+{
+ ahc->msgout_len = 0;
+ ahc->msgin_index = 0;
+ ahc->msg_type = MSG_TYPE_NONE;
+ if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0) {
+ /*
+ * The target didn't care to respond to our
+ * message request, so clear ATN.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ }
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+ ahc_outb(ahc, SEQ_FLAGS2,
+ ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahc_handle_message_phase(struct ahc_softc *ahc)
+{
+ struct ahc_devinfo devinfo;
+ u_int bus_phase;
+ int end_session;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+ end_session = FALSE;
+ bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+
+reswitch:
+ switch (ahc->msg_type) {
+ case MSG_TYPE_INITIATOR_MSGOUT:
+ {
+ int lastbyte;
+ int phasemis;
+ int msgdone;
+
+ if (ahc->msgout_len == 0)
+ panic("HOST_MSG_LOOP interrupt with no active message");
+
+ phasemis = bus_phase != P_MESGOUT;
+ if (phasemis) {
+ if (bus_phase == P_MESGIN) {
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ ahc->send_msg_perror = FALSE;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+ ahc->msgin_index = 0;
+ goto reswitch;
+ }
+ end_session = TRUE;
+ break;
+ }
+
+ if (ahc->send_msg_perror) {
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
+ break;
+ }
+
+ msgdone = ahc->msgout_index == ahc->msgout_len;
+ if (msgdone) {
+ /*
+ * The target has requested a retry.
+ * Re-assert ATN, reset our message index to
+ * 0, and try again.
+ */
+ ahc->msgout_index = 0;
+ ahc_assert_atn(ahc);
+ }
+
+ lastbyte = ahc->msgout_index == (ahc->msgout_len - 1);
+ if (lastbyte) {
+ /* Last byte is signified by dropping ATN */
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ }
+
+ /*
+ * Clear our interrupt status and present
+ * the next byte on the bus.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+ break;
+ }
+ case MSG_TYPE_INITIATOR_MSGIN:
+ {
+ int phasemis;
+ int message_done;
+
+ phasemis = bus_phase != P_MESGIN;
+
+ if (phasemis) {
+ ahc->msgin_index = 0;
+ if (bus_phase == P_MESGOUT
+ && (ahc->send_msg_perror == TRUE
+ || (ahc->msgout_len != 0
+ && ahc->msgout_index == 0))) {
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ goto reswitch;
+ }
+ end_session = TRUE;
+ break;
+ }
+
+ /* Pull the byte in without acking it */
+ ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
+
+ message_done = ahc_parse_msg(ahc, &devinfo);
+
+ if (message_done) {
+ /*
+ * Clear our incoming message buffer in case there
+ * is another message following this one.
+ */
+ ahc->msgin_index = 0;
+
+ /*
+ * If this message illicited a response,
+ * assert ATN so the target takes us to the
+ * message out phase.
+ */
+ if (ahc->msgout_len != 0)
+ ahc_assert_atn(ahc);
+ } else
+ ahc->msgin_index++;
+
+ if (message_done == MSGLOOP_TERMINATED) {
+ end_session = TRUE;
+ } else {
+ /* Ack the byte */
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_inb(ahc, SCSIDATL);
+ }
+ break;
+ }
+ case MSG_TYPE_TARGET_MSGIN:
+ {
+ int msgdone;
+ int msgout_request;
+
+ if (ahc->msgout_len == 0)
+ panic("Target MSGIN with no active message");
+
+ /*
+ * If we interrupted a mesgout session, the initiator
+ * will not know this until our first REQ. So, we
+ * only honor mesgout requests after we've sent our
+ * first byte.
+ */
+ if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0
+ && ahc->msgout_index > 0)
+ msgout_request = TRUE;
+ else
+ msgout_request = FALSE;
+
+ if (msgout_request) {
+
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahc->msg_type = MSG_TYPE_TARGET_MSGOUT;
+ ahc_outb(ahc, SCSISIGO, P_MESGOUT | BSYO);
+ ahc->msgin_index = 0;
+ /* Dummy read to REQ for first byte */
+ ahc_inb(ahc, SCSIDATL);
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ break;
+ }
+
+ msgdone = ahc->msgout_index == ahc->msgout_len;
+ if (msgdone) {
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+ end_session = TRUE;
+ break;
+ }
+
+ /*
+ * Present the next byte on the bus.
+ */
+ ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+ break;
+ }
+ case MSG_TYPE_TARGET_MSGOUT:
+ {
+ int lastbyte;
+ int msgdone;
+
+ /*
+ * The initiator signals that this is
+ * the last byte by dropping ATN.
+ */
+ lastbyte = (ahc_inb(ahc, SCSISIGI) & ATNI) == 0;
+
+ /*
+ * Read the latched byte, but turn off SPIOEN first
+ * so that we don't inadvertently cause a REQ for the
+ * next byte.
+ */
+ ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+ ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL);
+ msgdone = ahc_parse_msg(ahc, &devinfo);
+ if (msgdone == MSGLOOP_TERMINATED) {
+ /*
+ * The message is *really* done in that it caused
+ * us to go to bus free. The sequencer has already
+ * been reset at this point, so pull the ejection
+ * handle.
+ */
+ return;
+ }
+
+ ahc->msgin_index++;
+
+ /*
+ * XXX Read spec about initiator dropping ATN too soon
+ * and use msgdone to detect it.
+ */
+ if (msgdone == MSGLOOP_MSGCOMPLETE) {
+ ahc->msgin_index = 0;
+
+ /*
+ * If this message illicited a response, transition
+ * to the Message in phase and send it.
+ */
+ if (ahc->msgout_len != 0) {
+ ahc_outb(ahc, SCSISIGO, P_MESGIN | BSYO);
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+ ahc->msgin_index = 0;
+ break;
+ }
+ }
+
+ if (lastbyte)
+ end_session = TRUE;
+ else {
+ /* Ask for the next byte. */
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ }
+
+ break;
+ }
+ default:
+ panic("Unknown REQINIT message type");
+ }
+
+ if (end_session) {
+ ahc_clear_msg_state(ahc);
+ ahc_outb(ahc, RETURN_1, EXIT_MSG_LOOP);
+ } else
+ ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message. If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
+{
+ int found;
+ u_int index;
+
+ found = FALSE;
+ index = 0;
+
+ while (index < ahc->msgout_len) {
+ if (ahc->msgout_buf[index] == MSG_EXTENDED) {
+ u_int end_index;
+
+ end_index = index + 1 + ahc->msgout_buf[index + 1];
+ if (ahc->msgout_buf[index+2] == msgval
+ && type == AHCMSG_EXT) {
+
+ if (full) {
+ if (ahc->msgout_index > end_index)
+ found = TRUE;
+ } else if (ahc->msgout_index > index)
+ found = TRUE;
+ }
+ index = end_index;
+ } else if (ahc->msgout_buf[index] >= MSG_SIMPLE_TASK
+ && ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+ /* Skip tag type and tag id or residue param*/
+ index += 2;
+ } else {
+ /* Single byte message */
+ if (type == AHCMSG_1B
+ && ahc->msgout_buf[index] == msgval
+ && ahc->msgout_index > index)
+ found = TRUE;
+ index++;
+ }
+
+ if (found)
+ break;
+ }
+ return (found);
+}
+
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
+static int
+ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ int reject;
+ int done;
+ int response;
+ u_int targ_scsirate;
+
+ done = MSGLOOP_IN_PROG;
+ response = FALSE;
+ reject = FALSE;
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ targ_scsirate = tinfo->scsirate;
+
+ /*
+ * Parse as much of the message as is availible,
+ * rejecting it if we don't support it. When
+ * the entire message is availible and has been
+ * handled, return MSGLOOP_MSGCOMPLETE, indicating
+ * that we have parsed an entire message.
+ *
+ * In the case of extended messages, we accept the length
+ * byte outright and perform more checking once we know the
+ * extended message type.
+ */
+ switch (ahc->msgin_buf[0]) {
+ case MSG_DISCONNECT:
+ case MSG_SAVEDATAPOINTER:
+ case MSG_CMDCOMPLETE:
+ case MSG_RESTOREPOINTERS:
+ case MSG_IGN_WIDE_RESIDUE:
+ /*
+ * End our message loop as these are messages
+ * the sequencer handles on its own.
+ */
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MSG_MESSAGE_REJECT:
+ response = ahc_handle_msg_reject(ahc, devinfo);
+ /* FALLTHROUGH */
+ case MSG_NOOP:
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ case MSG_EXTENDED:
+ {
+ /* Wait for enough of the message to begin validation */
+ if (ahc->msgin_index < 2)
+ break;
+ switch (ahc->msgin_buf[2]) {
+ case MSG_EXT_SDTR:
+ {
+ struct ahc_syncrate *syncrate;
+ u_int period;
+ u_int ppr_options;
+ u_int offset;
+ u_int saved_offset;
+
+ if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have both args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_SDTR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahc->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+ break;
+
+ period = ahc->msgin_buf[3];
+ ppr_options = 0;
+ saved_offset = offset = ahc->msgin_buf[4];
+ syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+ &ppr_options,
+ devinfo->role);
+ ahc_validate_offset(ahc, tinfo, syncrate, &offset,
+ targ_scsirate & WIDEXFER,
+ devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received "
+ "SDTR period %x, offset %x\n\t"
+ "Filtered to period %x, offset %x\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ ahc->msgin_buf[3], saved_offset,
+ period, offset);
+ }
+ ahc_set_syncrate(ahc, devinfo,
+ syncrate, period,
+ offset, ppr_options,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+
+ /*
+ * See if we initiated Sync Negotiation
+ * and didn't have to fall down to async
+ * transfers.
+ */
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+ /* We started it */
+ if (saved_offset != offset) {
+ /* Went too low - force async */
+ reject = TRUE;
+ }
+ } else {
+ /*
+ * Send our own SDTR in reply
+ */
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated SDTR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_sdtr(ahc, devinfo,
+ period, offset);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ }
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ case MSG_EXT_WDTR:
+ {
+ u_int bus_width;
+ u_int saved_width;
+ u_int sending_reply;
+
+ sending_reply = FALSE;
+ if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have our arg before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_WDTR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+ break;
+
+ bus_width = ahc->msgin_buf[3];
+ saved_width = bus_width;
+ ahc_validate_width(ahc, tinfo, &bus_width,
+ devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received WDTR "
+ "%x filtered to %x\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, bus_width);
+ }
+
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+ /*
+ * Don't send a WDTR back to the
+ * target, since we asked first.
+ * If the width went higher than our
+ * request, reject it.
+ */
+ if (saved_width > bus_width) {
+ reject = TRUE;
+ printf("(%s:%c:%d:%d): requested %dBit "
+ "transfers. Rejecting...\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ 8 * (0x01 << bus_width));
+ bus_width = 0;
+ }
+ } else {
+ /*
+ * Send our own WDTR in reply
+ */
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated WDTR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_wdtr(ahc, devinfo, bus_width);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ sending_reply = TRUE;
+ }
+ ahc_set_width(ahc, devinfo, bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ /* After a wide message, we are async */
+ ahc_set_syncrate(ahc, devinfo,
+ /*syncrate*/NULL, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHC_TRANS_ACTIVE, /*paused*/TRUE);
+ if (sending_reply == FALSE && reject == FALSE) {
+
+ if (tinfo->goal.period) {
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_build_transfer_msg(ahc, devinfo);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ }
+ }
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ case MSG_EXT_PPR:
+ {
+ struct ahc_syncrate *syncrate;
+ u_int period;
+ u_int offset;
+ u_int bus_width;
+ u_int ppr_options;
+ u_int saved_width;
+ u_int saved_offset;
+ u_int saved_ppr_options;
+
+ if (ahc->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have all args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_PPR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahc->msgin_index < (MSG_EXT_PPR_LEN + 1))
+ break;
+
+ period = ahc->msgin_buf[3];
+ offset = ahc->msgin_buf[5];
+ bus_width = ahc->msgin_buf[6];
+ saved_width = bus_width;
+ ppr_options = ahc->msgin_buf[7];
+ /*
+ * According to the spec, a DT only
+ * period factor with no DT option
+ * set implies async.
+ */
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && period == 9)
+ offset = 0;
+ saved_ppr_options = ppr_options;
+ saved_offset = offset;
+
+ /*
+ * Mask out any options we don't support
+ * on any controller. Transfer options are
+ * only available if we are negotiating wide.
+ */
+ ppr_options &= MSG_EXT_PPR_DT_REQ;
+ if (bus_width == 0)
+ ppr_options = 0;
+
+ ahc_validate_width(ahc, tinfo, &bus_width,
+ devinfo->role);
+ syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+ &ppr_options,
+ devinfo->role);
+ ahc_validate_offset(ahc, tinfo, syncrate,
+ &offset, bus_width,
+ devinfo->role);
+
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, TRUE)) {
+ /*
+ * If we are unable to do any of the
+ * requested options (we went too low),
+ * then we'll have to reject the message.
+ */
+ if (saved_width > bus_width
+ || saved_offset != offset
+ || saved_ppr_options != ppr_options) {
+ reject = TRUE;
+ period = 0;
+ offset = 0;
+ bus_width = 0;
+ ppr_options = 0;
+ syncrate = NULL;
+ }
+ } else {
+ if (devinfo->role != ROLE_TARGET)
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated PPR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ else
+ printf("(%s:%c:%d:%d): Initiator "
+ "Initiated PPR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_ppr(ahc, devinfo, period, offset,
+ bus_width, ppr_options);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ }
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received PPR width %x, "
+ "period %x, offset %x,options %x\n"
+ "\tFiltered to width %x, period %x, "
+ "offset %x, options %x\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, ahc->msgin_buf[3],
+ saved_offset, saved_ppr_options,
+ bus_width, period, offset, ppr_options);
+ }
+ ahc_set_width(ahc, devinfo, bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahc_set_syncrate(ahc, devinfo,
+ syncrate, period,
+ offset, ppr_options,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ default:
+ /* Unknown extended message. Reject it. */
+ reject = TRUE;
+ break;
+ }
+ break;
+ }
+#ifdef AHC_TARGET_MODE
+ case MSG_BUS_DEV_RESET:
+ ahc_handle_devreset(ahc, devinfo,
+ CAM_BDR_SENT,
+ "Bus Device Reset Received",
+ /*verbose_level*/0);
+ ahc_restart(ahc);
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MSG_ABORT_TAG:
+ case MSG_ABORT:
+ case MSG_CLEAR_QUEUE:
+ {
+ int tag;
+
+ /* Target mode messages */
+ if (devinfo->role != ROLE_TARGET) {
+ reject = TRUE;
+ break;
+ }
+ tag = SCB_LIST_NULL;
+ if (ahc->msgin_buf[0] == MSG_ABORT_TAG)
+ tag = ahc_inb(ahc, INITIATOR_TAG);
+ ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+ devinfo->lun, tag, ROLE_TARGET,
+ CAM_REQ_ABORTED);
+
+ tstate = ahc->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ struct ahc_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[devinfo->lun];
+ if (lstate != NULL) {
+ ahc_queue_lstate_event(ahc, lstate,
+ devinfo->our_scsiid,
+ ahc->msgin_buf[0],
+ /*arg*/tag);
+ ahc_send_lstate_events(ahc, lstate);
+ }
+ }
+ ahc_restart(ahc);
+ done = MSGLOOP_TERMINATED;
+ break;
+ }
+#endif
+ case MSG_TERM_IO_PROC:
+ default:
+ reject = TRUE;
+ break;
+ }
+
+ if (reject) {
+ /*
+ * Setup to reject the message.
+ */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 1;
+ ahc->msgout_buf[0] = MSG_MESSAGE_REJECT;
+ done = MSGLOOP_MSGCOMPLETE;
+ response = TRUE;
+ }
+
+ if (done != MSGLOOP_IN_PROG && !response)
+ /* Clear the outgoing message buffer */
+ ahc->msgout_len = 0;
+
+ return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ /*
+ * What we care about here is if we had an
+ * outstanding SDTR or WDTR message for this
+ * target. If we did, this is a signal that
+ * the target is refusing negotiation.
+ */
+ struct scb *scb;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int scb_index;
+ u_int last_msg;
+ int response = 0;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ /* Might be necessary */
+ last_msg = ahc_inb(ahc, LAST_MSG);
+
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+ /*
+ * Target does not support the PPR message.
+ * Attempt to negotiate SPI-2 style.
+ */
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): PPR Rejected. "
+ "Trying WDTR/SDTR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ tinfo->goal.ppr_options = 0;
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_build_transfer_msg(ahc, devinfo);
+ ahc->msgout_index = 0;
+ response = 1;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+ /* note 8bit xfers */
+ printf("(%s:%c:%d:%d): refuses WIDE negotiation. Using "
+ "8bit transfers\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target, devinfo->lun);
+ ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ /*
+ * No need to clear the sync rate. If the target
+ * did not accept the command, our syncrate is
+ * unaffected. If the target started the negotiation,
+ * but rejected our response, we already cleared the
+ * sync rate before sending our WDTR.
+ */
+ if (tinfo->goal.period) {
+
+ /* Start the sync negotiation */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_build_transfer_msg(ahc, devinfo);
+ ahc->msgout_index = 0;
+ response = 1;
+ }
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+ /* note asynch xfers and clear flag */
+ ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ printf("(%s:%c:%d:%d): refuses synchronous negotiation. "
+ "Using asynchronous transfers\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ } else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+ int tag_type;
+ int mask;
+
+ tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+ if (tag_type == MSG_SIMPLE_TASK) {
+ printf("(%s:%c:%d:%d): refuses tagged commands. "
+ "Performing non-tagged I/O\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target, devinfo->lun);
+ ahc_set_tags(ahc, devinfo, AHC_QUEUE_NONE);
+ mask = ~0x23;
+ } else {
+ printf("(%s:%c:%d:%d): refuses %s tagged commands. "
+ "Performing simple queue tagged I/O only\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ devinfo->lun, tag_type == MSG_ORDERED_TASK
+ ? "ordered" : "head of queue");
+ ahc_set_tags(ahc, devinfo, AHC_QUEUE_BASIC);
+ mask = ~0x03;
+ }
+
+ /*
+ * Resend the identify for this CCB as the target
+ * may believe that the selection is invalid otherwise.
+ */
+ ahc_outb(ahc, SCB_CONTROL,
+ ahc_inb(ahc, SCB_CONTROL) & mask);
+ scb->hscb->control &= mask;
+ ahc_set_transaction_tag(scb, /*enabled*/FALSE,
+ /*type*/MSG_SIMPLE_TASK);
+ ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
+ ahc_assert_atn(ahc);
+
+ /*
+ * This transaction is now at the head of
+ * the untagged queue for this target.
+ */
+ if ((ahc->flags & AHC_SCB_BTT) == 0) {
+ struct scb_tailq *untagged_q;
+
+ untagged_q =
+ &(ahc->untagged_queues[devinfo->target_offset]);
+ TAILQ_INSERT_HEAD(untagged_q, scb, links.tqe);
+ scb->flags |= SCB_UNTAGGEDQ;
+ }
+ ahc_busy_tcl(ahc, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+ scb->hscb->tag);
+
+ /*
+ * Requeue all tagged commands for this target
+ * currently in our posession so they can be
+ * converted to untagged commands.
+ */
+ ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ } else {
+ /*
+ * Otherwise, we ignore it.
+ */
+ printf("%s:%c:%d: Message reject for %x -- ignored\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ last_msg);
+ }
+ return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ u_int scb_index;
+ struct scb *scb;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ /*
+ * XXX Actually check data direction in the sequencer?
+ * Perhaps add datadir to some spare bits in the hscb?
+ */
+ if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
+ || ahc_get_transfer_dir(scb) != CAM_DIR_IN) {
+ /*
+ * Ignore the message if we haven't
+ * seen an appropriate data phase yet.
+ */
+ } else {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing. Otherwise, subtract a byte
+ * and update the residual count accordingly.
+ */
+ uint32_t sgptr;
+
+ sgptr = ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+ if ((sgptr & SG_LIST_NULL) != 0
+ && ahc_inb(ahc, DATA_COUNT_ODD) == 1) {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing.
+ */
+ } else {
+ struct ahc_dma_seg *sg;
+ uint32_t data_cnt;
+ uint32_t data_addr;
+ uint32_t sglen;
+
+ /* Pull in the rest of the sgptr */
+ sgptr |= (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8);
+ sgptr &= SG_PTR_MASK;
+ data_cnt = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+3) << 24)
+ | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+1) << 8)
+ | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT));
+
+ data_addr = (ahc_inb(ahc, SHADDR + 3) << 24)
+ | (ahc_inb(ahc, SHADDR + 2) << 16)
+ | (ahc_inb(ahc, SHADDR + 1) << 8)
+ | (ahc_inb(ahc, SHADDR));
+
+ data_cnt += 1;
+ data_addr -= 1;
+
+ sg = ahc_sg_bus_to_virt(scb, sgptr);
+ /*
+ * The residual sg ptr points to the next S/G
+ * to load so we must go back one.
+ */
+ sg--;
+ sglen = ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+ if (sg != scb->sg_list
+ && sglen < (data_cnt & AHC_SG_LEN_MASK)) {
+
+ sg--;
+ sglen = ahc_le32toh(sg->len);
+ /*
+ * Preserve High Address and SG_LIST bits
+ * while setting the count to 1.
+ */
+ data_cnt = 1 | (sglen & (~AHC_SG_LEN_MASK));
+ data_addr = ahc_le32toh(sg->addr)
+ + (sglen & AHC_SG_LEN_MASK) - 1;
+
+ /*
+ * Increment sg so it points to the
+ * "next" sg.
+ */
+ sg++;
+ sgptr = ahc_sg_virt_to_bus(scb, sg);
+ ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 3,
+ sgptr >> 24);
+ ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 2,
+ sgptr >> 16);
+ ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 1,
+ sgptr >> 8);
+ ahc_outb(ahc, SCB_RESIDUAL_SGPTR, sgptr);
+ }
+
+ ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 3, data_cnt >> 24);
+ ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 2, data_cnt >> 16);
+ ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 1, data_cnt >> 8);
+ ahc_outb(ahc, SCB_RESIDUAL_DATACNT, data_cnt);
+ }
+ }
+}
+
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahc_reinitialize_dataptrs(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+ struct ahc_dma_seg *sg;
+ u_int scb_index;
+ uint32_t sgptr;
+ uint32_t resid;
+ uint32_t dataptr;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ sgptr = (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8)
+ | ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+
+ sgptr &= SG_PTR_MASK;
+ sg = ahc_sg_bus_to_virt(scb, sgptr);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ resid = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 1) << 8)
+ | ahc_inb(ahc, SCB_RESIDUAL_DATACNT);
+
+ dataptr = ahc_le32toh(sg->addr)
+ + (ahc_le32toh(sg->len) & AHC_SG_LEN_MASK)
+ - resid;
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ u_int dscommand1;
+
+ dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+ ahc_outb(ahc, HADDR,
+ (ahc_le32toh(sg->len) >> 24) & SG_HIGH_ADDR_BITS);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1);
+ }
+ ahc_outb(ahc, HADDR + 3, dataptr >> 24);
+ ahc_outb(ahc, HADDR + 2, dataptr >> 16);
+ ahc_outb(ahc, HADDR + 1, dataptr >> 8);
+ ahc_outb(ahc, HADDR, dataptr);
+ ahc_outb(ahc, HCNT + 2, resid >> 16);
+ ahc_outb(ahc, HCNT + 1, resid >> 8);
+ ahc_outb(ahc, HCNT, resid);
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ ahc_outb(ahc, STCNT + 2, resid >> 16);
+ ahc_outb(ahc, STCNT + 1, resid >> 8);
+ ahc_outb(ahc, STCNT, resid);
+ }
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ cam_status status, char *message, int verbose_level)
+{
+#ifdef AHC_TARGET_MODE
+ struct ahc_tmode_tstate* tstate;
+ u_int lun;
+#endif
+ int found;
+
+ found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
+ status);
+
+#ifdef AHC_TARGET_MODE
+ /*
+ * Send an immediate notify ccb to all target mord peripheral
+ * drivers affected by this action.
+ */
+ tstate = ahc->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[lun];
+ if (lstate == NULL)
+ continue;
+
+ ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid,
+ MSG_BUS_DEV_RESET, /*arg*/0);
+ ahc_send_lstate_events(ahc, lstate);
+ }
+ }
+#endif
+
+ /*
+ * Go back to async/narrow transfers and renegotiate.
+ */
+ ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR, /*paused*/TRUE);
+ ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0, /*ppr_options*/0,
+ AHC_TRANS_CUR, /*paused*/TRUE);
+
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
+
+ if (message != NULL
+ && (verbose_level <= bootverbose))
+ printf("%s: %s on %c:%d. %d SCBs aborted\n", ahc_name(ahc),
+ message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHC_TARGET_MODE
+static void
+ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct scb *scb)
+{
+
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+
+ if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+ ahc_build_transfer_msg(ahc, devinfo);
+ else
+ panic("ahc_intr: AWAITING target message with no message");
+
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahc_softc *
+ahc_alloc(void *platform_arg, char *name)
+{
+ struct ahc_softc *ahc;
+ int i;
+
+#ifndef __FreeBSD__
+ ahc = malloc(sizeof(*ahc), M_DEVBUF, M_NOWAIT);
+ if (!ahc) {
+ printf("aic7xxx: cannot malloc softc!\n");
+ free(name, M_DEVBUF);
+ return NULL;
+ }
+#else
+ ahc = device_get_softc((device_t)platform_arg);
+#endif
+ memset(ahc, 0, sizeof(*ahc));
+ ahc->seep_config = malloc(sizeof(*ahc->seep_config),
+ M_DEVBUF, M_NOWAIT);
+ if (ahc->seep_config == NULL) {
+#ifndef __FreeBSD__
+ free(ahc, M_DEVBUF);
+#endif
+ free(name, M_DEVBUF);
+ return (NULL);
+ }
+ LIST_INIT(&ahc->pending_scbs);
+ /* We don't know our unit number until the OSM sets it */
+ ahc->name = name;
+ ahc->unit = -1;
+ ahc->description = NULL;
+ ahc->channel = 'A';
+ ahc->channel_b = 'B';
+ ahc->chip = AHC_NONE;
+ ahc->features = AHC_FENONE;
+ ahc->bugs = AHC_BUGNONE;
+ ahc->flags = AHC_FNONE;
+
+ for (i = 0; i < AHC_NUM_TARGETS; i++)
+ TAILQ_INIT(&ahc->untagged_queues[i]);
+ if (ahc_platform_alloc(ahc, platform_arg) != 0) {
+ ahc_free(ahc);
+ ahc = NULL;
+ }
+ return (ahc);
+}
+
+int
+ahc_softc_init(struct ahc_softc *ahc)
+{
+
+ /* The IRQMS bit is only valid on VL and EISA chips */
+ if ((ahc->chip & AHC_PCI) == 0)
+ ahc->unpause = ahc_inb(ahc, HCNTRL) & IRQMS;
+ else
+ ahc->unpause = 0;
+ ahc->pause = ahc->unpause | PAUSE;
+ /* XXX The shared scb data stuff should be deprecated */
+ if (ahc->scb_data == NULL) {
+ ahc->scb_data = malloc(sizeof(*ahc->scb_data),
+ M_DEVBUF, M_NOWAIT);
+ if (ahc->scb_data == NULL)
+ return (ENOMEM);
+ memset(ahc->scb_data, 0, sizeof(*ahc->scb_data));
+ }
+
+ return (0);
+}
+
+void
+ahc_softc_insert(struct ahc_softc *ahc)
+{
+ struct ahc_softc *list_ahc;
+
+#if AHC_PCI_CONFIG > 0
+ /*
+ * Second Function PCI devices need to inherit some
+ * settings from function 0.
+ */
+ if ((ahc->chip & AHC_BUS_MASK) == AHC_PCI
+ && (ahc->features & AHC_MULTI_FUNC) != 0) {
+ TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+ ahc_dev_softc_t list_pci;
+ ahc_dev_softc_t pci;
+
+ list_pci = list_ahc->dev_softc;
+ pci = ahc->dev_softc;
+ if (ahc_get_pci_slot(list_pci) == ahc_get_pci_slot(pci)
+ && ahc_get_pci_bus(list_pci) == ahc_get_pci_bus(pci)) {
+ struct ahc_softc *master;
+ struct ahc_softc *slave;
+
+ if (ahc_get_pci_function(list_pci) == 0) {
+ master = list_ahc;
+ slave = ahc;
+ } else {
+ master = ahc;
+ slave = list_ahc;
+ }
+ slave->flags &= ~AHC_BIOS_ENABLED;
+ slave->flags |=
+ master->flags & AHC_BIOS_ENABLED;
+ slave->flags &= ~AHC_PRIMARY_CHANNEL;
+ slave->flags |=
+ master->flags & AHC_PRIMARY_CHANNEL;
+ break;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Insertion sort into our list of softcs.
+ */
+ list_ahc = TAILQ_FIRST(&ahc_tailq);
+ while (list_ahc != NULL
+ && ahc_softc_comp(list_ahc, ahc) <= 0)
+ list_ahc = TAILQ_NEXT(list_ahc, links);
+ if (list_ahc != NULL)
+ TAILQ_INSERT_BEFORE(list_ahc, ahc, links);
+ else
+ TAILQ_INSERT_TAIL(&ahc_tailq, ahc, links);
+ ahc->init_level++;
+}
+
+/*
+ * Verify that the passed in softc pointer is for a
+ * controller that is still configured.
+ */
+struct ahc_softc *
+ahc_find_softc(struct ahc_softc *ahc)
+{
+ struct ahc_softc *list_ahc;
+
+ TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+ if (list_ahc == ahc)
+ return (ahc);
+ }
+ return (NULL);
+}
+
+void
+ahc_set_unit(struct ahc_softc *ahc, int unit)
+{
+ ahc->unit = unit;
+}
+
+void
+ahc_set_name(struct ahc_softc *ahc, char *name)
+{
+ if (ahc->name != NULL)
+ free(ahc->name, M_DEVBUF);
+ ahc->name = name;
+}
+
+void
+ahc_free(struct ahc_softc *ahc)
+{
+ int i;
+
+ ahc_fini_scbdata(ahc);
+ switch (ahc->init_level) {
+ default:
+ case 5:
+ ahc_shutdown(ahc);
+ TAILQ_REMOVE(&ahc_tailq, ahc, links);
+ /* FALLTHROUGH */
+ case 4:
+ ahc_dmamap_unload(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap);
+ /* FALLTHROUGH */
+ case 3:
+ ahc_dmamem_free(ahc, ahc->shared_data_dmat, ahc->qoutfifo,
+ ahc->shared_data_dmamap);
+ ahc_dmamap_destroy(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap);
+ /* FALLTHROUGH */
+ case 2:
+ ahc_dma_tag_destroy(ahc, ahc->shared_data_dmat);
+ case 1:
+#ifndef __linux__
+ ahc_dma_tag_destroy(ahc, ahc->buffer_dmat);
+#endif
+ break;
+ case 0:
+ break;
+ }
+
+#ifndef __linux__
+ ahc_dma_tag_destroy(ahc, ahc->parent_dmat);
+#endif
+ ahc_platform_free(ahc);
+ for (i = 0; i < AHC_NUM_TARGETS; i++) {
+ struct ahc_tmode_tstate *tstate;
+
+ tstate = ahc->enabled_targets[i];
+ if (tstate != NULL) {
+#if AHC_TARGET_MODE
+ int j;
+
+ for (j = 0; j < AHC_NUM_LUNS; j++) {
+ struct ahc_tmode_lstate *lstate;
+
+ lstate = tstate->enabled_luns[j];
+ if (lstate != NULL) {
+ xpt_free_path(lstate->path);
+ free(lstate, M_DEVBUF);
+ }
+ }
+#endif
+ free(tstate, M_DEVBUF);
+ }
+ }
+#if AHC_TARGET_MODE
+ if (ahc->black_hole != NULL) {
+ xpt_free_path(ahc->black_hole->path);
+ free(ahc->black_hole, M_DEVBUF);
+ }
+#endif
+ if (ahc->name != NULL)
+ free(ahc->name, M_DEVBUF);
+ if (ahc->seep_config != NULL)
+ free(ahc->seep_config, M_DEVBUF);
+#ifndef __FreeBSD__
+ free(ahc, M_DEVBUF);
+#endif
+ return;
+}
+
+void
+ahc_shutdown(void *arg)
+{
+ struct ahc_softc *ahc;
+ int i;
+
+ ahc = (struct ahc_softc *)arg;
+
+ /* This will reset most registers to 0, but not all */
+ ahc_reset(ahc);
+ ahc_outb(ahc, SCSISEQ, 0);
+ ahc_outb(ahc, SXFRCTL0, 0);
+ ahc_outb(ahc, DSPCISTATUS, 0);
+
+ for (i = TARG_SCSIRATE; i < SCSICONF; i++)
+ ahc_outb(ahc, i, 0);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only available just after a reset.
+ */
+int
+ahc_reset(struct ahc_softc *ahc)
+{
+ u_int sblkctl;
+ u_int sxfrctl1_a, sxfrctl1_b;
+ int wait;
+
+ /*
+ * Preserve the value of the SXFRCTL1 register for all channels.
+ * It contains settings that affect termination and we don't want
+ * to disturb the integrity of the bus.
+ */
+ ahc_pause(ahc);
+ sxfrctl1_b = 0;
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7770) {
+ u_int sblkctl;
+
+ /*
+ * Save channel B's settings in case this chip
+ * is setup for TWIN channel operation.
+ */
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+ sxfrctl1_b = ahc_inb(ahc, SXFRCTL1);
+ ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+ }
+ sxfrctl1_a = ahc_inb(ahc, SXFRCTL1);
+
+ ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
+
+ /*
+ * Ensure that the reset has finished. We delay 1000us
+ * prior to reading the register to make sure the chip
+ * has sufficiently completed its reset to handle register
+ * accesses.
+ */
+ wait = 1000;
+ do {
+ ahc_delay(1000);
+ } while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK));
+
+ if (wait == 0) {
+ printf("%s: WARNING - Failed chip reset! "
+ "Trying to initialize anyway.\n", ahc_name(ahc));
+ }
+ ahc_outb(ahc, HCNTRL, ahc->pause);
+
+ /* Determine channel configuration */
+ sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
+ /* No Twin Channel PCI cards */
+ if ((ahc->chip & AHC_PCI) != 0)
+ sblkctl &= ~SELBUSB;
+ switch (sblkctl) {
+ case 0:
+ /* Single Narrow Channel */
+ break;
+ case 2:
+ /* Wide Channel */
+ ahc->features |= AHC_WIDE;
+ break;
+ case 8:
+ /* Twin Channel */
+ ahc->features |= AHC_TWIN;
+ break;
+ default:
+ printf(" Unsupported adapter type. Ignoring\n");
+ return(-1);
+ }
+
+ /*
+ * Reload sxfrctl1.
+ *
+ * We must always initialize STPWEN to 1 before we
+ * restore the saved values. STPWEN is initialized
+ * to a tri-state condition which can only be cleared
+ * by turning it on.
+ */
+ if ((ahc->features & AHC_TWIN) != 0) {
+ u_int sblkctl;
+
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+ ahc_outb(ahc, SXFRCTL1, sxfrctl1_b);
+ ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+ }
+ ahc_outb(ahc, SXFRCTL1, sxfrctl1_a);
+
+#ifdef AHC_DUMP_SEQ
+ if (ahc->init_level == 0)
+ ahc_dumpseq(ahc);
+#endif
+
+ return (0);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahc_probe_scbs(struct ahc_softc *ahc) {
+ int i;
+
+ for (i = 0; i < AHC_SCB_MAX; i++) {
+
+ ahc_outb(ahc, SCBPTR, i);
+ ahc_outb(ahc, SCB_BASE, i);
+ if (ahc_inb(ahc, SCB_BASE) != i)
+ break;
+ ahc_outb(ahc, SCBPTR, 0);
+ if (ahc_inb(ahc, SCB_BASE) != 0)
+ break;
+ }
+ return (i);
+}
+
+static void
+ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ bus_addr_t *baddr;
+
+ baddr = (bus_addr_t *)arg;
+ *baddr = segs->ds_addr;
+}
+
+static void
+ahc_build_free_scb_list(struct ahc_softc *ahc)
+{
+ int scbsize;
+ int i;
+
+ scbsize = 32;
+ if ((ahc->flags & AHC_LSCBS_ENABLED) != 0)
+ scbsize = 64;
+
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ int j;
+
+ ahc_outb(ahc, SCBPTR, i);
+
+ /*
+ * Touch all SCB bytes to avoid parity errors
+ * should one of our debugging routines read
+ * an otherwise uninitiatlized byte.
+ */
+ for (j = 0; j < scbsize; j++)
+ ahc_outb(ahc, SCB_BASE+j, 0xFF);
+
+ /* Clear the control byte. */
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ /* Set the next pointer */
+ if ((ahc->flags & AHC_PAGESCBS) != 0)
+ ahc_outb(ahc, SCB_NEXT, i+1);
+ else
+ ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+ /* Make the tag number, SCSIID, and lun invalid */
+ ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+ ahc_outb(ahc, SCB_SCSIID, 0xFF);
+ ahc_outb(ahc, SCB_LUN, 0xFF);
+ }
+
+ /* Make sure that the last SCB terminates the free list */
+ ahc_outb(ahc, SCBPTR, i-1);
+ ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+}
+
+static int
+ahc_init_scbdata(struct ahc_softc *ahc)
+{
+ struct scb_data *scb_data;
+
+ scb_data = ahc->scb_data;
+ SLIST_INIT(&scb_data->free_scbs);
+ SLIST_INIT(&scb_data->sg_maps);
+
+ /* Allocate SCB resources */
+ scb_data->scbarray =
+ (struct scb *)malloc(sizeof(struct scb) * AHC_SCB_MAX_ALLOC,
+ M_DEVBUF, M_NOWAIT);
+ if (scb_data->scbarray == NULL)
+ return (ENOMEM);
+ memset(scb_data->scbarray, 0, sizeof(struct scb) * AHC_SCB_MAX_ALLOC);
+
+ /* Determine the number of hardware SCBs and initialize them */
+
+ scb_data->maxhscbs = ahc_probe_scbs(ahc);
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ /* SCB 0 heads the free list */
+ ahc_outb(ahc, FREE_SCBH, 0);
+ } else {
+ ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL);
+ }
+
+ if (ahc->scb_data->maxhscbs == 0) {
+ printf("%s: No SCB space found\n", ahc_name(ahc));
+ return (ENXIO);
+ }
+
+ ahc_build_free_scb_list(ahc);
+
+ /*
+ * Create our DMA tags. These tags define the kinds of device
+ * accessible memory allocations and memory mappings we will
+ * need to perform during normal operation.
+ *
+ * Unless we need to further restrict the allocation, we rely
+ * on the restrictions of the parent dmat, hence the common
+ * use of MAXADDR and MAXSIZE.
+ */
+
+ /* DMA tag for our hardware scb structures */
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+ /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->hscb_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* Allocation for our hscbs */
+ if (ahc_dmamem_alloc(ahc, scb_data->hscb_dmat,
+ (void **)&scb_data->hscbs,
+ BUS_DMA_NOWAIT, &scb_data->hscb_dmamap) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* And permanently map them */
+ ahc_dmamap_load(ahc, scb_data->hscb_dmat, scb_data->hscb_dmamap,
+ scb_data->hscbs,
+ AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+ ahc_dmamap_cb, &scb_data->hscb_busaddr, /*flags*/0);
+
+ scb_data->init_level++;
+
+ /* DMA tag for our sense buffers */
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+ /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->sense_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* Allocate them */
+ if (ahc_dmamem_alloc(ahc, scb_data->sense_dmat,
+ (void **)&scb_data->sense,
+ BUS_DMA_NOWAIT, &scb_data->sense_dmamap) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* And permanently map them */
+ ahc_dmamap_load(ahc, scb_data->sense_dmat, scb_data->sense_dmamap,
+ scb_data->sense,
+ AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+ ahc_dmamap_cb, &scb_data->sense_busaddr, /*flags*/0);
+
+ scb_data->init_level++;
+
+ /* DMA tag for our S/G structures. We allocate in page sized chunks */
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ PAGE_SIZE, /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->sg_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* Perform initial CCB allocation */
+ memset(scb_data->hscbs, 0,
+ AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb));
+ ahc_alloc_scbs(ahc);
+
+ if (scb_data->numscbs == 0) {
+ printf("%s: ahc_init_scbdata - "
+ "Unable to allocate initial scbs\n",
+ ahc_name(ahc));
+ goto error_exit;
+ }
+
+ /*
+ * Tell the sequencer which SCB will be the next one it receives.
+ */
+ ahc->next_queued_scb = ahc_get_scb(ahc);
+ ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+ /*
+ * Note that we were successfull
+ */
+ return (0);
+
+error_exit:
+
+ return (ENOMEM);
+}
+
+static void
+ahc_fini_scbdata(struct ahc_softc *ahc)
+{
+ struct scb_data *scb_data;
+
+ scb_data = ahc->scb_data;
+ if (scb_data == NULL)
+ return;
+
+ switch (scb_data->init_level) {
+ default:
+ case 7:
+ {
+ struct sg_map_node *sg_map;
+
+ while ((sg_map = SLIST_FIRST(&scb_data->sg_maps))!= NULL) {
+ SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+ ahc_dmamap_unload(ahc, scb_data->sg_dmat,
+ sg_map->sg_dmamap);
+ ahc_dmamem_free(ahc, scb_data->sg_dmat,
+ sg_map->sg_vaddr,
+ sg_map->sg_dmamap);
+ free(sg_map, M_DEVBUF);
+ }
+ ahc_dma_tag_destroy(ahc, scb_data->sg_dmat);
+ }
+ case 6:
+ ahc_dmamap_unload(ahc, scb_data->sense_dmat,
+ scb_data->sense_dmamap);
+ case 5:
+ ahc_dmamem_free(ahc, scb_data->sense_dmat, scb_data->sense,
+ scb_data->sense_dmamap);
+ ahc_dmamap_destroy(ahc, scb_data->sense_dmat,
+ scb_data->sense_dmamap);
+ case 4:
+ ahc_dma_tag_destroy(ahc, scb_data->sense_dmat);
+ case 3:
+ ahc_dmamap_unload(ahc, scb_data->hscb_dmat,
+ scb_data->hscb_dmamap);
+ case 2:
+ ahc_dmamem_free(ahc, scb_data->hscb_dmat, scb_data->hscbs,
+ scb_data->hscb_dmamap);
+ ahc_dmamap_destroy(ahc, scb_data->hscb_dmat,
+ scb_data->hscb_dmamap);
+ case 1:
+ ahc_dma_tag_destroy(ahc, scb_data->hscb_dmat);
+ break;
+ case 0:
+ break;
+ }
+ if (scb_data->scbarray != NULL)
+ free(scb_data->scbarray, M_DEVBUF);
+}
+
+void
+ahc_alloc_scbs(struct ahc_softc *ahc)
+{
+ struct scb_data *scb_data;
+ struct scb *next_scb;
+ struct sg_map_node *sg_map;
+ bus_addr_t physaddr;
+ struct ahc_dma_seg *segs;
+ int newcount;
+ int i;
+
+ scb_data = ahc->scb_data;
+ if (scb_data->numscbs >= AHC_SCB_MAX_ALLOC)
+ /* Can't allocate any more */
+ return;
+
+ next_scb = &scb_data->scbarray[scb_data->numscbs];
+
+ sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
+
+ if (sg_map == NULL)
+ return;
+
+ /* Allocate S/G space for the next batch of SCBS */
+ if (ahc_dmamem_alloc(ahc, scb_data->sg_dmat,
+ (void **)&sg_map->sg_vaddr,
+ BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) {
+ free(sg_map, M_DEVBUF);
+ return;
+ }
+
+ SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+ ahc_dmamap_load(ahc, scb_data->sg_dmat, sg_map->sg_dmamap,
+ sg_map->sg_vaddr, PAGE_SIZE, ahc_dmamap_cb,
+ &sg_map->sg_physaddr, /*flags*/0);
+
+ segs = sg_map->sg_vaddr;
+ physaddr = sg_map->sg_physaddr;
+
+ newcount = (PAGE_SIZE / (AHC_NSEG * sizeof(struct ahc_dma_seg)));
+ newcount = MIN(newcount, (AHC_SCB_MAX_ALLOC - scb_data->numscbs));
+ for (i = 0; i < newcount; i++) {
+ struct scb_platform_data *pdata;
+#ifndef __linux__
+ int error;
+#endif
+ pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
+ M_DEVBUF, M_NOWAIT);
+ if (pdata == NULL)
+ break;
+ next_scb->platform_data = pdata;
+ next_scb->sg_map = sg_map;
+ next_scb->sg_list = segs;
+ /*
+ * The sequencer always starts with the second entry.
+ * The first entry is embedded in the scb.
+ */
+ next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
+ next_scb->ahc_softc = ahc;
+ next_scb->flags = SCB_FREE;
+#ifndef __linux__
+ error = ahc_dmamap_create(ahc, ahc->buffer_dmat, /*flags*/0,
+ &next_scb->dmamap);
+ if (error != 0)
+ break;
+#endif
+ next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
+ next_scb->hscb->tag = ahc->scb_data->numscbs;
+ SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs,
+ next_scb, links.sle);
+ segs += AHC_NSEG;
+ physaddr += (AHC_NSEG * sizeof(struct ahc_dma_seg));
+ next_scb++;
+ ahc->scb_data->numscbs++;
+ }
+}
+
+void
+ahc_controller_info(struct ahc_softc *ahc, char *buf)
+{
+ int len;
+
+ len = sprintf(buf, "%s: ", ahc_chip_names[ahc->chip & AHC_CHIPID_MASK]);
+ buf += len;
+ if ((ahc->features & AHC_TWIN) != 0)
+ len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
+ "B SCSI Id=%d, primary %c, ",
+ ahc->our_id, ahc->our_id_b,
+ (ahc->flags & AHC_PRIMARY_CHANNEL) + 'A');
+ else {
+ const char *speed;
+ const char *type;
+
+ speed = "";
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ speed = "Ultra ";
+ } else if ((ahc->features & AHC_DT) != 0) {
+ speed = "Ultra160 ";
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ speed = "Ultra2 ";
+ }
+ if ((ahc->features & AHC_WIDE) != 0) {
+ type = "Wide";
+ } else {
+ type = "Single";
+ }
+ len = sprintf(buf, "%s%s Channel %c, SCSI Id=%d, ",
+ speed, type, ahc->channel, ahc->our_id);
+ }
+ buf += len;
+
+ if ((ahc->flags & AHC_PAGESCBS) != 0)
+ sprintf(buf, "%d/%d SCBs",
+ ahc->scb_data->maxhscbs, AHC_MAX_QUEUE);
+ else
+ sprintf(buf, "%d SCBs", ahc->scb_data->maxhscbs);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahc_init(struct ahc_softc *ahc)
+{
+ int max_targ;
+ int i;
+ int term;
+ u_int scsi_conf;
+ u_int scsiseq_template;
+ u_int ultraenb;
+ u_int discenable;
+ u_int tagenable;
+ size_t driver_data_size;
+ uint32_t physaddr;
+
+#ifdef AHC_DEBUG_SEQUENCER
+ ahc->flags |= AHC_SEQUENCER_DEBUG;
+#endif
+
+#ifdef AHC_PRINT_SRAM
+ printf("Scratch Ram:");
+ for (i = 0x20; i < 0x5f; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", ahc_inb(ahc, i));
+ }
+ if ((ahc->features & AHC_MORE_SRAM) != 0) {
+ for (i = 0x70; i < 0x7f; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", ahc_inb(ahc, i));
+ }
+ }
+ printf ("\n");
+ /*
+ * Reading uninitialized scratch ram may
+ * generate parity errors.
+ */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+#endif
+ max_targ = 15;
+
+ /*
+ * Assume we have a board at this stage and it has been reset.
+ */
+ if ((ahc->flags & AHC_USEDEFAULTS) != 0)
+ ahc->our_id = ahc->our_id_b = 7;
+
+ /*
+ * Default to allowing initiator operations.
+ */
+ ahc->flags |= AHC_INITIATORROLE;
+
+ /*
+ * Only allow target mode features if this unit has them enabled.
+ */
+ if ((AHC_TMODE_ENABLE & (0x1 << ahc->unit)) == 0)
+ ahc->features &= ~AHC_TARGETMODE;
+
+#ifndef __linux__
+ /* DMA tag for mapping buffers into device visible space. */
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/MAXBSIZE, /*nsegments*/AHC_NSEG,
+ /*maxsegsz*/AHC_MAXTRANSFER_SIZE,
+ /*flags*/BUS_DMA_ALLOCNOW,
+ &ahc->buffer_dmat) != 0) {
+ return (ENOMEM);
+ }
+#endif
+
+ ahc->init_level++;
+
+ /*
+ * DMA tag for our command fifos and other data in system memory
+ * the card's sequencer must be able to access. For initiator
+ * roles, we need to allocate space for the the qinfifo and qoutfifo.
+ * The qinfifo and qoutfifo are composed of 256 1 byte elements.
+ * When providing for the target mode role, we must additionally
+ * provide space for the incoming target command fifo and an extra
+ * byte to deal with a dma bug in some chip versions.
+ */
+ driver_data_size = 2 * 256 * sizeof(uint8_t);
+ if ((ahc->features & AHC_TARGETMODE) != 0)
+ driver_data_size += AHC_TMODE_CMDS * sizeof(struct target_cmd)
+ + /*DMA WideOdd Bug Buffer*/1;
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ driver_data_size,
+ /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &ahc->shared_data_dmat) != 0) {
+ return (ENOMEM);
+ }
+
+ ahc->init_level++;
+
+ /* Allocation of driver data */
+ if (ahc_dmamem_alloc(ahc, ahc->shared_data_dmat,
+ (void **)&ahc->qoutfifo,
+ BUS_DMA_NOWAIT, &ahc->shared_data_dmamap) != 0) {
+ return (ENOMEM);
+ }
+
+ ahc->init_level++;
+
+ /* And permanently map it in */
+ ahc_dmamap_load(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+ ahc->qoutfifo, driver_data_size, ahc_dmamap_cb,
+ &ahc->shared_data_busaddr, /*flags*/0);
+
+ if ((ahc->features & AHC_TARGETMODE) != 0) {
+ ahc->targetcmds = (struct target_cmd *)ahc->qoutfifo;
+ ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[AHC_TMODE_CMDS];
+ ahc->dma_bug_buf = ahc->shared_data_busaddr
+ + driver_data_size - 1;
+ /* All target command blocks start out invalid. */
+ for (i = 0; i < AHC_TMODE_CMDS; i++)
+ ahc->targetcmds[i].cmd_valid = 0;
+ ahc_sync_tqinfifo(ahc, BUS_DMASYNC_PREREAD);
+ ahc->tqinfifonext = 1;
+ ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1);
+ ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+ ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[256];
+ }
+ ahc->qinfifo = &ahc->qoutfifo[256];
+
+ ahc->init_level++;
+
+ /* Allocate SCB data now that buffer_dmat is initialized */
+ if (ahc->scb_data->maxhscbs == 0)
+ if (ahc_init_scbdata(ahc) != 0)
+ return (ENOMEM);
+
+ /*
+ * Allocate a tstate to house information for our
+ * initiator presence on the bus as well as the user
+ * data for any target mode initiator.
+ */
+ if (ahc_alloc_tstate(ahc, ahc->our_id, 'A') == NULL) {
+ printf("%s: unable to allocate ahc_tmode_tstate. "
+ "Failing attach\n", ahc_name(ahc));
+ return (ENOMEM);
+ }
+
+ if ((ahc->features & AHC_TWIN) != 0) {
+ if (ahc_alloc_tstate(ahc, ahc->our_id_b, 'B') == NULL) {
+ printf("%s: unable to allocate ahc_tmode_tstate. "
+ "Failing attach\n", ahc_name(ahc));
+ return (ENOMEM);
+ }
+ }
+
+ ahc_outb(ahc, SEQ_FLAGS, 0);
+ ahc_outb(ahc, SEQ_FLAGS2, 0);
+
+ if (ahc->scb_data->maxhscbs < AHC_SCB_MAX_ALLOC) {
+ ahc->flags |= AHC_PAGESCBS;
+ } else {
+ ahc->flags &= ~AHC_PAGESCBS;
+ }
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWMISC) {
+ printf("%s: hardware scb %d bytes; kernel scb %d bytes; "
+ "ahc_dma %d bytes\n",
+ ahc_name(ahc),
+ sizeof(struct hardware_scb),
+ sizeof(struct scb),
+ sizeof(struct ahc_dma_seg));
+ }
+#endif /* AHC_DEBUG */
+
+ /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
+ if (ahc->features & AHC_TWIN) {
+
+ /*
+ * The device is gated to channel B after a chip reset,
+ * so set those values first
+ */
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+ term = (ahc->flags & AHC_TERM_ENB_B) != 0 ? STPWEN : 0;
+ ahc_outb(ahc, SCSIID, ahc->our_id_b);
+ scsi_conf = ahc_inb(ahc, SCSICONF + 1);
+ ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+ |term|ahc->seltime_b|ENSTIMER|ACTNEGEN);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+ ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+ ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+ if ((scsi_conf & RESET_SCSI) != 0
+ && (ahc->flags & AHC_INITIATORROLE) != 0)
+ ahc->flags |= AHC_RESET_BUS_B;
+
+ /* Select Channel A */
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+ }
+ term = (ahc->flags & AHC_TERM_ENB_A) != 0 ? STPWEN : 0;
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+ else
+ ahc_outb(ahc, SCSIID, ahc->our_id);
+ scsi_conf = ahc_inb(ahc, SCSICONF);
+ ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+ |term|ahc->seltime
+ |ENSTIMER|ACTNEGEN);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+ ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+ ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+ if ((scsi_conf & RESET_SCSI) != 0
+ && (ahc->flags & AHC_INITIATORROLE) != 0)
+ ahc->flags |= AHC_RESET_BUS_A;
+
+ /*
+ * Look at the information that board initialization or
+ * the board bios has left us.
+ */
+ ultraenb = 0;
+ tagenable = ALL_TARGETS_MASK;
+
+ /* Grab the disconnection disable table and invert it for our needs */
+ if ((ahc->flags & AHC_USEDEFAULTS) != 0) {
+ printf("%s: Host Adapter Bios disabled. Using default SCSI "
+ "device parameters\n", ahc_name(ahc));
+ ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B|
+ AHC_TERM_ENB_A|AHC_TERM_ENB_B;
+ discenable = ALL_TARGETS_MASK;
+ if ((ahc->features & AHC_ULTRA) != 0)
+ ultraenb = ALL_TARGETS_MASK;
+ } else {
+ discenable = ~((ahc_inb(ahc, DISC_DSB + 1) << 8)
+ | ahc_inb(ahc, DISC_DSB));
+ if ((ahc->features & (AHC_ULTRA|AHC_ULTRA2)) != 0)
+ ultraenb = (ahc_inb(ahc, ULTRA_ENB + 1) << 8)
+ | ahc_inb(ahc, ULTRA_ENB);
+ }
+
+ if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+ max_targ = 7;
+
+ for (i = 0; i <= max_targ; i++) {
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_id;
+ char channel;
+
+ channel = 'A';
+ our_id = ahc->our_id;
+ target_id = i;
+ if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+ channel = 'B';
+ our_id = ahc->our_id_b;
+ target_id = i % 8;
+ }
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+ target_id, &tstate);
+ /* Default to async narrow across the board */
+ memset(tinfo, 0, sizeof(*tinfo));
+ if (ahc->flags & AHC_USEDEFAULTS) {
+ if ((ahc->features & AHC_WIDE) != 0)
+ tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+ /*
+ * These will be truncated when we determine the
+ * connection type we have with the target.
+ */
+ tinfo->user.period = ahc_syncrates->period;
+ tinfo->user.offset = ~0;
+ } else {
+ u_int scsirate;
+ uint16_t mask;
+
+ /* Take the settings leftover in scratch RAM. */
+ scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
+ mask = (0x01 << i);
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int offset;
+ u_int maxsync;
+
+ if ((scsirate & SOFS) == 0x0F) {
+ /*
+ * Haven't negotiated yet,
+ * so the format is different.
+ */
+ scsirate = (scsirate & SXFR) >> 4
+ | (ultraenb & mask)
+ ? 0x08 : 0x0
+ | (scsirate & WIDEXFER);
+ offset = MAX_OFFSET_ULTRA2;
+ } else
+ offset = ahc_inb(ahc, TARG_OFFSET + i);
+ if ((scsirate & ~WIDEXFER) == 0 && offset != 0)
+ /* Set to the lowest sync rate, 5MHz */
+ scsirate |= 0x1c;
+ maxsync = AHC_SYNCRATE_ULTRA2;
+ if ((ahc->features & AHC_DT) != 0)
+ maxsync = AHC_SYNCRATE_DT;
+ tinfo->user.period =
+ ahc_find_period(ahc, scsirate, maxsync);
+ if (offset == 0)
+ tinfo->user.period = 0;
+ else
+ tinfo->user.offset = ~0;
+ if ((scsirate & SXFR_ULTRA2) <= 8/*10MHz*/
+ && (ahc->features & AHC_DT) != 0)
+ tinfo->user.ppr_options =
+ MSG_EXT_PPR_DT_REQ;
+ } else if ((scsirate & SOFS) != 0) {
+ if ((scsirate & SXFR) == 0x40
+ && (ultraenb & mask) != 0) {
+ /* Treat 10MHz as a non-ultra speed */
+ scsirate &= ~SXFR;
+ ultraenb &= ~mask;
+ }
+ tinfo->user.period =
+ ahc_find_period(ahc, scsirate,
+ (ultraenb & mask)
+ ? AHC_SYNCRATE_ULTRA
+ : AHC_SYNCRATE_FAST);
+ if (tinfo->user.period != 0)
+ tinfo->user.offset = ~0;
+ }
+ if (tinfo->user.period == 0)
+ tinfo->user.offset = 0;
+ if ((scsirate & WIDEXFER) != 0
+ && (ahc->features & AHC_WIDE) != 0)
+ tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+ tinfo->user.protocol_version = 4;
+ if ((ahc->features & AHC_DT) != 0)
+ tinfo->user.transport_version = 3;
+ else
+ tinfo->user.transport_version = 2;
+ tinfo->goal.protocol_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->curr.protocol_version = 2;
+ tinfo->curr.transport_version = 2;
+ }
+ tstate->ultraenb = ultraenb;
+ }
+ ahc->user_discenable = discenable;
+ ahc->user_tagenable = tagenable;
+
+ /* There are no untagged SCBs active yet. */
+ for (i = 0; i < 16; i++) {
+ ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, 0));
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ int lun;
+
+ /*
+ * The SCB based BTT allows an entry per
+ * target and lun pair.
+ */
+ for (lun = 1; lun < AHC_NUM_LUNS; lun++)
+ ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, lun));
+ }
+ }
+
+ /* All of our queues are empty */
+ for (i = 0; i < 256; i++)
+ ahc->qoutfifo[i] = SCB_LIST_NULL;
+ ahc_sync_qoutfifo(ahc, BUS_DMASYNC_PREREAD);
+
+ for (i = 0; i < 256; i++)
+ ahc->qinfifo[i] = SCB_LIST_NULL;
+
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ ahc_outb(ahc, TARGID, 0);
+ ahc_outb(ahc, TARGID + 1, 0);
+ }
+
+ /*
+ * Tell the sequencer where it can find our arrays in memory.
+ */
+ physaddr = ahc->scb_data->hscb_busaddr;
+ ahc_outb(ahc, HSCB_ADDR, physaddr & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 1, (physaddr >> 8) & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 2, (physaddr >> 16) & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+ physaddr = ahc->shared_data_busaddr;
+ ahc_outb(ahc, SHARED_DATA_ADDR, physaddr & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR + 1, (physaddr >> 8) & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR + 2, (physaddr >> 16) & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+ /*
+ * Initialize the group code to command length table.
+ * This overrides the values in TARG_SCSIRATE, so only
+ * setup the table after we have processed that information.
+ */
+ ahc_outb(ahc, CMDSIZE_TABLE, 5);
+ ahc_outb(ahc, CMDSIZE_TABLE + 1, 9);
+ ahc_outb(ahc, CMDSIZE_TABLE + 2, 9);
+ ahc_outb(ahc, CMDSIZE_TABLE + 3, 0);
+ ahc_outb(ahc, CMDSIZE_TABLE + 4, 15);
+ ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
+ ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
+ ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
+
+ /* Tell the sequencer of our initial queue positions */
+ ahc_outb(ahc, KERNEL_QINPOS, 0);
+ ahc_outb(ahc, QINPOS, 0);
+ ahc_outb(ahc, QOUTPOS, 0);
+
+ /*
+ * Use the built in queue management registers
+ * if they are available.
+ */
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, QOFF_CTLSTA, SCB_QSIZE_256);
+ ahc_outb(ahc, SDSCB_QOFF, 0);
+ ahc_outb(ahc, SNSCB_QOFF, 0);
+ ahc_outb(ahc, HNSCB_QOFF, 0);
+ }
+
+
+ /* We don't have any waiting selections */
+ ahc_outb(ahc, WAITING_SCBH, SCB_LIST_NULL);
+
+ /* Our disconnection list is empty too */
+ ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
+
+ /* Message out buffer starts empty */
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+
+ /*
+ * Setup the allowed SCSI Sequences based on operational mode.
+ * If we are a target, we'll enalbe select in operations once
+ * we've had a lun enabled.
+ */
+ scsiseq_template = ENSELO|ENAUTOATNO|ENAUTOATNP;
+ if ((ahc->flags & AHC_INITIATORROLE) != 0)
+ scsiseq_template |= ENRSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq_template);
+
+ /*
+ * Load the Sequencer program and Enable the adapter
+ * in "fast" mode.
+ */
+ if (bootverbose)
+ printf("%s: Downloading Sequencer Program...",
+ ahc_name(ahc));
+
+ ahc_loadseq(ahc);
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ int wait;
+
+ /*
+ * Wait for up to 500ms for our transceivers
+ * to settle. If the adapter does not have
+ * a cable attached, the tranceivers may
+ * never settle, so don't complain if we
+ * fail here.
+ */
+ ahc_pause(ahc);
+ for (wait = 5000;
+ (ahc_inb(ahc, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+ wait--)
+ ahc_delay(100);
+ ahc_unpause(ahc);
+ }
+ return (0);
+}
+
+void
+ahc_intr_enable(struct ahc_softc *ahc, int enable)
+{
+ u_int hcntrl;
+
+ hcntrl = ahc_inb(ahc, HCNTRL);
+ hcntrl &= ~INTEN;
+ ahc->pause &= ~INTEN;
+ ahc->unpause &= ~INTEN;
+ if (enable) {
+ hcntrl |= INTEN;
+ ahc->pause |= INTEN;
+ ahc->unpause |= INTEN;
+ }
+ ahc_outb(ahc, HCNTRL, hcntrl);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahc_pause_and_flushwork(struct ahc_softc *ahc)
+{
+ int intstat;
+ int maxloops;
+
+ maxloops = 1000;
+ ahc->flags |= AHC_ALL_INTERRUPTS;
+ intstat = 0;
+ do {
+ ahc_intr(ahc);
+ ahc_pause(ahc);
+ ahc_clear_critical_section(ahc);
+ if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
+ break;
+ maxloops--;
+ } while (((intstat = ahc_inb(ahc, INTSTAT)) & INT_PEND) && --maxloops);
+ if (maxloops == 0) {
+ printf("Infinite interrupt loop, INTSTAT = %x",
+ ahc_inb(ahc, INTSTAT));
+ }
+ ahc_platform_flushwork(ahc);
+ ahc->flags &= ~AHC_ALL_INTERRUPTS;
+}
+
+int
+ahc_suspend(struct ahc_softc *ahc)
+{
+ uint8_t *ptr;
+ int i;
+
+ ahc_pause_and_flushwork(ahc);
+
+ if (LIST_FIRST(&ahc->pending_scbs) != NULL)
+ return (EBUSY);
+
+#if AHC_TARGET_MODE
+ /*
+ * XXX What about ATIOs that have not yet been serviced?
+ * Perhaps we should just refuse to be suspended if we
+ * are acting in a target role.
+ */
+ if (ahc->pending_device != NULL)
+ return (EBUSY);
+#endif
+
+ /* Save volatile registers */
+ if ((ahc->features & AHC_TWIN) != 0) {
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+ ahc->suspend_state.channel[1].scsiseq = ahc_inb(ahc, SCSISEQ);
+ ahc->suspend_state.channel[1].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+ ahc->suspend_state.channel[1].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+ ahc->suspend_state.channel[1].simode0 = ahc_inb(ahc, SIMODE0);
+ ahc->suspend_state.channel[1].simode1 = ahc_inb(ahc, SIMODE1);
+ ahc->suspend_state.channel[1].seltimer = ahc_inb(ahc, SELTIMER);
+ ahc->suspend_state.channel[1].seqctl = ahc_inb(ahc, SEQCTL);
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+ }
+ ahc->suspend_state.channel[0].scsiseq = ahc_inb(ahc, SCSISEQ);
+ ahc->suspend_state.channel[0].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+ ahc->suspend_state.channel[0].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+ ahc->suspend_state.channel[0].simode0 = ahc_inb(ahc, SIMODE0);
+ ahc->suspend_state.channel[0].simode1 = ahc_inb(ahc, SIMODE1);
+ ahc->suspend_state.channel[0].seltimer = ahc_inb(ahc, SELTIMER);
+ ahc->suspend_state.channel[0].seqctl = ahc_inb(ahc, SEQCTL);
+
+ if ((ahc->chip & AHC_PCI) != 0) {
+ ahc->suspend_state.dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+ ahc->suspend_state.dspcistatus = ahc_inb(ahc, DSPCISTATUS);
+ }
+
+ if ((ahc->features & AHC_DT) != 0) {
+ u_int sfunct;
+
+ sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+ ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+ ahc->suspend_state.optionmode = ahc_inb(ahc, OPTIONMODE);
+ ahc_outb(ahc, SFUNCT, sfunct);
+ ahc->suspend_state.crccontrol1 = ahc_inb(ahc, CRCCONTROL1);
+ }
+
+ if ((ahc->features & AHC_MULTI_FUNC) != 0)
+ ahc->suspend_state.scbbaddr = ahc_inb(ahc, SCBBADDR);
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc->suspend_state.dff_thrsh = ahc_inb(ahc, DFF_THRSH);
+
+ ptr = ahc->suspend_state.scratch_ram;
+ for (i = 0; i < 64; i++)
+ *ptr++ = ahc_inb(ahc, SRAM_BASE + i);
+
+ if ((ahc->features & AHC_MORE_SRAM) != 0) {
+ for (i = 0; i < 16; i++)
+ *ptr++ = ahc_inb(ahc, TARG_OFFSET + i);
+ }
+
+ ptr = ahc->suspend_state.btt;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ for (i = 0;i < AHC_NUM_TARGETS; i++) {
+ int j;
+
+ for (j = 0;j < AHC_NUM_LUNS; j++) {
+ u_int tcl;
+
+ tcl = BUILD_TCL(i << 4, j);
+ *ptr = ahc_index_busy_tcl(ahc, tcl);
+ }
+ }
+ }
+ ahc_shutdown(ahc);
+ return (0);
+}
+
+int
+ahc_resume(struct ahc_softc *ahc)
+{
+ uint8_t *ptr;
+ int i;
+
+ ahc_reset(ahc);
+
+ ahc_build_free_scb_list(ahc);
+
+ /* Restore volatile registers */
+ if ((ahc->features & AHC_TWIN) != 0) {
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+ ahc_outb(ahc, SCSIID, ahc->our_id);
+ ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[1].scsiseq);
+ ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[1].sxfrctl0);
+ ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[1].sxfrctl1);
+ ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[1].simode0);
+ ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[1].simode1);
+ ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[1].seltimer);
+ ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[1].seqctl);
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+ }
+ ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[0].scsiseq);
+ ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[0].sxfrctl0);
+ ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[0].sxfrctl1);
+ ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[0].simode0);
+ ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[0].simode1);
+ ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[0].seltimer);
+ ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[0].seqctl);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+ else
+ ahc_outb(ahc, SCSIID, ahc->our_id);
+
+ if ((ahc->chip & AHC_PCI) != 0) {
+ ahc_outb(ahc, DSCOMMAND0, ahc->suspend_state.dscommand0);
+ ahc_outb(ahc, DSPCISTATUS, ahc->suspend_state.dspcistatus);
+ }
+
+ if ((ahc->features & AHC_DT) != 0) {
+ u_int sfunct;
+
+ sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+ ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+ ahc_outb(ahc, OPTIONMODE, ahc->suspend_state.optionmode);
+ ahc_outb(ahc, SFUNCT, sfunct);
+ ahc_outb(ahc, CRCCONTROL1, ahc->suspend_state.crccontrol1);
+ }
+
+ if ((ahc->features & AHC_MULTI_FUNC) != 0)
+ ahc_outb(ahc, SCBBADDR, ahc->suspend_state.scbbaddr);
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, DFF_THRSH, ahc->suspend_state.dff_thrsh);
+
+ ptr = ahc->suspend_state.scratch_ram;
+ for (i = 0; i < 64; i++)
+ ahc_outb(ahc, SRAM_BASE + i, *ptr++);
+
+ if ((ahc->features & AHC_MORE_SRAM) != 0) {
+ for (i = 0; i < 16; i++)
+ ahc_outb(ahc, TARG_OFFSET + i, *ptr++);
+ }
+
+ ptr = ahc->suspend_state.btt;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ for (i = 0;i < AHC_NUM_TARGETS; i++) {
+ int j;
+
+ for (j = 0;j < AHC_NUM_LUNS; j++) {
+ u_int tcl;
+
+ tcl = BUILD_TCL(i << 4, j);
+ ahc_busy_tcl(ahc, tcl, *ptr);
+ }
+ }
+ }
+ return (0);
+}
+
+/************************** Busy Target Table *********************************/
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ * Optionally, clear the entry.
+ */
+u_int
+ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+ u_int scbid;
+ u_int target_offset;
+
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+ scbid = ahc_inb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl));
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ } else {
+ target_offset = TCL_TARGET_OFFSET(tcl);
+ scbid = ahc_inb(ahc, BUSY_TARGETS + target_offset);
+ }
+
+ return (scbid);
+}
+
+void
+ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+ u_int target_offset;
+
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+ ahc_outb(ahc, SCB_64_BTT+TCL_TARGET_OFFSET(tcl), SCB_LIST_NULL);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ } else {
+ target_offset = TCL_TARGET_OFFSET(tcl);
+ ahc_outb(ahc, BUSY_TARGETS + target_offset, SCB_LIST_NULL);
+ }
+}
+
+void
+ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
+{
+ u_int target_offset;
+
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+ ahc_outb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl), scbid);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ } else {
+ target_offset = TCL_TARGET_OFFSET(tcl);
+ ahc_outb(ahc, BUSY_TARGETS + target_offset, scbid);
+ }
+}
+
+/************************** SCB and SCB queue management **********************/
+int
+ahc_match_scb(struct ahc_softc *ahc, struct scb *scb, int target,
+ char channel, int lun, u_int tag, role_t role)
+{
+ int targ = SCB_GET_TARGET(ahc, scb);
+ char chan = SCB_GET_CHANNEL(ahc, scb);
+ int slun = SCB_GET_LUN(scb);
+ int match;
+
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+ if (match != 0)
+ match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+ if (match != 0) {
+#if AHC_TARGET_MODE
+ int group;
+
+ group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+ if (role == ROLE_INITIATOR) {
+ match = (group != XPT_FC_GROUP_TMODE)
+ && ((tag == scb->hscb->tag)
+ || (tag == SCB_LIST_NULL));
+ } else if (role == ROLE_TARGET) {
+ match = (group == XPT_FC_GROUP_TMODE)
+ && ((tag == scb->io_ctx->csio.tag_id)
+ || (tag == SCB_LIST_NULL));
+ }
+#else /* !AHC_TARGET_MODE */
+ match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+#endif /* AHC_TARGET_MODE */
+ }
+
+ return match;
+}
+
+void
+ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+ int target;
+ char channel;
+ int lun;
+
+ target = SCB_GET_TARGET(ahc, scb);
+ lun = SCB_GET_LUN(scb);
+ channel = SCB_GET_CHANNEL(ahc, scb);
+
+ ahc_search_qinfifo(ahc, target, channel, lun,
+ /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+ CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ ahc_platform_freeze_devq(ahc, scb);
+}
+
+void
+ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct scb *prev_scb;
+
+ prev_scb = NULL;
+ if (ahc_qinfifo_count(ahc) != 0) {
+ u_int prev_tag;
+ uint8_t prev_pos;
+
+ prev_pos = ahc->qinfifonext - 1;
+ prev_tag = ahc->qinfifo[prev_pos];
+ prev_scb = ahc_lookup_scb(ahc, prev_tag);
+ }
+ ahc_qinfifo_requeue(ahc, prev_scb, scb);
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ }
+}
+
+static void
+ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
+ struct scb *scb)
+{
+ if (prev_scb == NULL) {
+ ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+ } else {
+ prev_scb->hscb->next = scb->hscb->tag;
+ ahc_sync_scb(ahc, prev_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ }
+ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+ scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+ ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+static int
+ahc_qinfifo_count(struct ahc_softc *ahc)
+{
+ u_int8_t qinpos;
+ u_int8_t diff;
+
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ qinpos = ahc_inb(ahc, SNSCB_QOFF);
+ ahc_outb(ahc, SNSCB_QOFF, qinpos);
+ } else
+ qinpos = ahc_inb(ahc, QINPOS);
+ diff = ahc->qinfifonext - qinpos;
+ return (diff);
+}
+
+int
+ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status,
+ ahc_search_action action)
+{
+ struct scb *scb;
+ struct scb *prev_scb;
+ uint8_t qinstart;
+ uint8_t qinpos;
+ uint8_t qintail;
+ uint8_t next;
+ uint8_t prev;
+ uint8_t curscbptr;
+ int found;
+ int have_qregs;
+
+ qintail = ahc->qinfifonext;
+ have_qregs = (ahc->features & AHC_QUEUE_REGS) != 0;
+ if (have_qregs) {
+ qinstart = ahc_inb(ahc, SNSCB_QOFF);
+ ahc_outb(ahc, SNSCB_QOFF, qinstart);
+ } else
+ qinstart = ahc_inb(ahc, QINPOS);
+ qinpos = qinstart;
+ found = 0;
+ prev_scb = NULL;
+
+ if (action == SEARCH_COMPLETE) {
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahc_freeze_untagged_queues(ahc);
+ }
+
+ /*
+ * Start with an empty queue. Entries that are not chosen
+ * for removal will be re-added to the queue as we go.
+ */
+ ahc->qinfifonext = qinpos;
+ ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+ while (qinpos != qintail) {
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]);
+ if (scb == NULL) {
+ printf("qinpos = %d, SCB index = %d\n",
+ qinpos, ahc->qinfifo[qinpos]);
+ panic("Loop 1\n");
+ }
+
+ if (ahc_match_scb(ahc, scb, target, channel, lun, tag, role)) {
+ /*
+ * We found an scb that needs to be acted on.
+ */
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahc_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scb, status);
+ cstat = ahc_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahc_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in qinfifo\n");
+ ahc_done(ahc, scb);
+
+ /* FALLTHROUGH */
+ }
+ case SEARCH_REMOVE:
+ break;
+ case SEARCH_COUNT:
+ ahc_qinfifo_requeue(ahc, prev_scb, scb);
+ prev_scb = scb;
+ break;
+ }
+ } else {
+ ahc_qinfifo_requeue(ahc, prev_scb, scb);
+ prev_scb = scb;
+ }
+ qinpos++;
+ }
+
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ }
+
+ if (action != SEARCH_COUNT
+ && (found != 0)
+ && (qinstart != ahc->qinfifonext)) {
+ /*
+ * The sequencer may be in the process of dmaing
+ * down the SCB at the beginning of the queue.
+ * This could be problematic if either the first,
+ * or the second SCB is removed from the queue
+ * (the first SCB includes a pointer to the "next"
+ * SCB to dma). If we have removed any entries, swap
+ * the first element in the queue with the next HSCB
+ * so the sequencer will notice that NEXT_QUEUED_SCB
+ * has changed during its dma attempt and will retry
+ * the DMA.
+ */
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinstart]);
+
+ if (scb == NULL) {
+ printf("found = %d, qinstart = %d, qinfifionext = %d\n",
+ found, qinstart, ahc->qinfifonext);
+ panic("First/Second Qinfifo fixup\n");
+ }
+ /*
+ * ahc_swap_with_next_hscb forces our next pointer to
+ * point to the reserved SCB for future commands. Save
+ * and restore our original next pointer to maintain
+ * queue integrity.
+ */
+ next = scb->hscb->next;
+ ahc->scb_data->scbindex[scb->hscb->tag] = NULL;
+ ahc_swap_with_next_hscb(ahc, scb);
+ scb->hscb->next = next;
+ ahc->qinfifo[qinstart] = scb->hscb->tag;
+
+ /* Tell the card about the new head of the qinfifo. */
+ ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+
+ /* Fixup the tail "next" pointer. */
+ qintail = ahc->qinfifonext - 1;
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qintail]);
+ scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+ }
+
+ /*
+ * Search waiting for selection list.
+ */
+ curscbptr = ahc_inb(ahc, SCBPTR);
+ next = ahc_inb(ahc, WAITING_SCBH); /* Start at head of list. */
+ prev = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL) {
+ uint8_t scb_index;
+
+ ahc_outb(ahc, SCBPTR, next);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ if (scb_index >= ahc->scb_data->numscbs) {
+ printf("Waiting List inconsistency. "
+ "SCB index == %d, yet numscbs == %d.",
+ scb_index, ahc->scb_data->numscbs);
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ }
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("scb_index = %d, next = %d\n",
+ scb_index, next);
+ panic("Waiting List traversal\n");
+ }
+ if (ahc_match_scb(ahc, scb, target, channel,
+ lun, SCB_LIST_NULL, role)) {
+ /*
+ * We found an scb that needs to be acted on.
+ */
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahc_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scb,
+ status);
+ cstat = ahc_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahc_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in Waiting List\n");
+ ahc_done(ahc, scb);
+ /* FALLTHROUGH */
+ }
+ case SEARCH_REMOVE:
+ next = ahc_rem_wscb(ahc, next, prev);
+ break;
+ case SEARCH_COUNT:
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ break;
+ }
+ } else {
+
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ }
+ ahc_outb(ahc, SCBPTR, curscbptr);
+
+ found += ahc_search_untagged_queues(ahc, /*ahc_io_ctx_t*/NULL, target,
+ channel, lun, status, action);
+
+ if (action == SEARCH_COMPLETE)
+ ahc_release_untagged_queues(ahc);
+ return (found);
+}
+
+int
+ahc_search_untagged_queues(struct ahc_softc *ahc, ahc_io_ctx_t ctx,
+ int target, char channel, int lun, uint32_t status,
+ ahc_search_action action)
+{
+ struct scb *scb;
+ int maxtarget;
+ int found;
+ int i;
+
+ if (action == SEARCH_COMPLETE) {
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahc_freeze_untagged_queues(ahc);
+ }
+
+ found = 0;
+ i = 0;
+ if ((ahc->flags & AHC_SCB_BTT) == 0) {
+
+ maxtarget = 16;
+ if (target != CAM_TARGET_WILDCARD) {
+
+ i = target;
+ if (channel == 'B')
+ i += 8;
+ maxtarget = i + 1;
+ }
+ } else {
+ maxtarget = 0;
+ }
+
+ for (; i < maxtarget; i++) {
+ struct scb_tailq *untagged_q;
+ struct scb *next_scb;
+
+ untagged_q = &(ahc->untagged_queues[i]);
+ next_scb = TAILQ_FIRST(untagged_q);
+ while (next_scb != NULL) {
+
+ scb = next_scb;
+ next_scb = TAILQ_NEXT(scb, links.tqe);
+
+ /*
+ * The head of the list may be the currently
+ * active untagged command for a device.
+ * We're only searching for commands that
+ * have not been started. A transaction
+ * marked active but still in the qinfifo
+ * is removed by the qinfifo scanning code
+ * above.
+ */
+ if ((scb->flags & SCB_ACTIVE) != 0)
+ continue;
+
+ if (ahc_match_scb(ahc, scb, target, channel, lun,
+ SCB_LIST_NULL, ROLE_INITIATOR) == 0
+ || (ctx != NULL && ctx != scb->io_ctx))
+ continue;
+
+ /*
+ * We found an scb that needs to be acted on.
+ */
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahc_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scb, status);
+ cstat = ahc_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahc_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in untaggedQ\n");
+ ahc_done(ahc, scb);
+ break;
+ }
+ case SEARCH_REMOVE:
+ TAILQ_REMOVE(untagged_q, scb, links.tqe);
+ break;
+ case SEARCH_COUNT:
+ break;
+ }
+ }
+ }
+
+ if (action == SEARCH_COMPLETE)
+ ahc_release_untagged_queues(ahc);
+ return (found);
+}
+
+int
+ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, int stop_on_first, int remove,
+ int save_state)
+{
+ struct scb *scbp;
+ u_int next;
+ u_int prev;
+ u_int count;
+ u_int active_scb;
+
+ count = 0;
+ next = ahc_inb(ahc, DISCONNECTED_SCBH);
+ prev = SCB_LIST_NULL;
+
+ if (save_state) {
+ /* restore this when we're done */
+ active_scb = ahc_inb(ahc, SCBPTR);
+ } else
+ /* Silence compiler */
+ active_scb = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL) {
+ u_int scb_index;
+
+ ahc_outb(ahc, SCBPTR, next);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ if (scb_index >= ahc->scb_data->numscbs) {
+ printf("Disconnected List inconsistency. "
+ "SCB index == %d, yet numscbs == %d.",
+ scb_index, ahc->scb_data->numscbs);
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ }
+
+ if (next == prev) {
+ panic("Disconnected List Loop. "
+ "cur SCBPTR == %x, prev SCBPTR == %x.",
+ next, prev);
+ }
+ scbp = ahc_lookup_scb(ahc, scb_index);
+ if (ahc_match_scb(ahc, scbp, target, channel, lun,
+ tag, ROLE_INITIATOR)) {
+ count++;
+ if (remove) {
+ next =
+ ahc_rem_scb_from_disc_list(ahc, prev, next);
+ } else {
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ if (stop_on_first)
+ break;
+ } else {
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ }
+ if (save_state)
+ ahc_outb(ahc, SCBPTR, active_scb);
+ return (count);
+}
+
+/*
+ * Remove an SCB from the on chip list of disconnected transactions.
+ * This is empty/unused if we are not performing SCB paging.
+ */
+static u_int
+ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
+{
+ u_int next;
+
+ ahc_outb(ahc, SCBPTR, scbptr);
+ next = ahc_inb(ahc, SCB_NEXT);
+
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ ahc_add_curscb_to_free_list(ahc);
+
+ if (prev != SCB_LIST_NULL) {
+ ahc_outb(ahc, SCBPTR, prev);
+ ahc_outb(ahc, SCB_NEXT, next);
+ } else
+ ahc_outb(ahc, DISCONNECTED_SCBH, next);
+
+ return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs. This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
+{
+ /*
+ * Invalidate the tag so that our abort
+ * routines don't think it's active.
+ */
+ ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
+ ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
+ }
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
+{
+ u_int curscb, next;
+
+ /*
+ * Select the SCB we want to abort and
+ * pull the next pointer out of it.
+ */
+ curscb = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, scbpos);
+ next = ahc_inb(ahc, SCB_NEXT);
+
+ /* Clear the necessary fields */
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ ahc_add_curscb_to_free_list(ahc);
+
+ /* update the waiting list */
+ if (prev == SCB_LIST_NULL) {
+ /* First in the list */
+ ahc_outb(ahc, WAITING_SCBH, next);
+
+ /*
+ * Ensure we aren't attempting to perform
+ * selection for this entry.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else {
+ /*
+ * Select the scb that pointed to us
+ * and update its next pointer.
+ */
+ ahc_outb(ahc, SCBPTR, prev);
+ ahc_outb(ahc, SCB_NEXT, next);
+ }
+
+ /*
+ * Point us back at the original scb position.
+ */
+ ahc_outb(ahc, SCBPTR, curscb);
+ return next;
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG. This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+int
+ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status)
+{
+ struct scb *scbp;
+ struct scb *scbp_next;
+ u_int active_scb;
+ int i, j;
+ int maxtarget;
+ int minlun;
+ int maxlun;
+
+ int found;
+
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahc_freeze_untagged_queues(ahc);
+
+ /* restore this when we're done */
+ active_scb = ahc_inb(ahc, SCBPTR);
+
+ found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL,
+ role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ /*
+ * Clean out the busy target table for any untagged commands.
+ */
+ i = 0;
+ maxtarget = 16;
+ if (target != CAM_TARGET_WILDCARD) {
+ i = target;
+ if (channel == 'B')
+ i += 8;
+ maxtarget = i + 1;
+ }
+
+ if (lun == CAM_LUN_WILDCARD) {
+
+ /*
+ * Unless we are using an SCB based
+ * busy targets table, there is only
+ * one table entry for all luns of
+ * a target.
+ */
+ minlun = 0;
+ maxlun = 1;
+ if ((ahc->flags & AHC_SCB_BTT) != 0)
+ maxlun = AHC_NUM_LUNS;
+ } else {
+ minlun = lun;
+ maxlun = lun + 1;
+ }
+
+ if (role != ROLE_TARGET) {
+ for (;i < maxtarget; i++) {
+ for (j = minlun;j < maxlun; j++) {
+ u_int scbid;
+ u_int tcl;
+
+ tcl = BUILD_TCL(i << 4, j);
+ scbid = ahc_index_busy_tcl(ahc, tcl);
+ scbp = ahc_lookup_scb(ahc, scbid);
+ if (scbp == NULL
+ || ahc_match_scb(ahc, scbp, target, channel,
+ lun, tag, role) == 0)
+ continue;
+ ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, j));
+ }
+ }
+
+ /*
+ * Go through the disconnected list and remove any entries we
+ * have queued for completion, 0'ing their control byte too.
+ * We save the active SCB and restore it ourselves, so there
+ * is no reason for this search to restore it too.
+ */
+ ahc_search_disc_list(ahc, target, channel, lun, tag,
+ /*stop_on_first*/FALSE, /*remove*/TRUE,
+ /*save_state*/FALSE);
+ }
+
+ /*
+ * Go through the hardware SCB array looking for commands that
+ * were active but not on any list. In some cases, these remnants
+ * might not still have mappings in the scbindex array (e.g. unexpected
+ * bus free with the same scb queued for an abort). Don't hold this
+ * against them.
+ */
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ u_int scbid;
+
+ ahc_outb(ahc, SCBPTR, i);
+ scbid = ahc_inb(ahc, SCB_TAG);
+ scbp = ahc_lookup_scb(ahc, scbid);
+ if ((scbp == NULL && scbid != SCB_LIST_NULL)
+ || (scbp != NULL
+ && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)))
+ ahc_add_curscb_to_free_list(ahc);
+ }
+
+ /*
+ * Go through the pending CCB list and look for
+ * commands for this target that are still active.
+ * These are other tagged commands that were
+ * disconnected when the reset occurred.
+ */
+ scbp_next = LIST_FIRST(&ahc->pending_scbs);
+ while (scbp_next != NULL) {
+ scbp = scbp_next;
+ scbp_next = LIST_NEXT(scbp, pending_links);
+ if (ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) {
+ cam_status ostat;
+
+ ostat = ahc_get_transaction_status(scbp);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scbp, status);
+ if (ahc_get_transaction_status(scbp) != CAM_REQ_CMP)
+ ahc_freeze_scb(scbp);
+ if ((scbp->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB on pending list\n");
+ ahc_done(ahc, scbp);
+ found++;
+ }
+ }
+ ahc_outb(ahc, SCBPTR, active_scb);
+ ahc_platform_abort_scbs(ahc, target, channel, lun, tag, role, status);
+ ahc_release_untagged_queues(ahc);
+ return found;
+}
+
+static void
+ahc_reset_current_bus(struct ahc_softc *ahc)
+{
+ uint8_t scsiseq;
+
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
+ ahc_flush_device_writes(ahc);
+ ahc_delay(AHC_BUSRESET_DELAY);
+ /* Turn off the bus reset */
+ ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
+
+ ahc_clear_intstat(ahc);
+
+ /* Re-enable reset interrupts */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
+}
+
+int
+ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
+{
+ struct ahc_devinfo devinfo;
+ u_int initiator, target, max_scsiid;
+ u_int sblkctl;
+ u_int scsiseq;
+ u_int simode1;
+ int found;
+ int restart_needed;
+ char cur_channel;
+
+ ahc->pending_device = NULL;
+
+ ahc_compile_devinfo(&devinfo,
+ CAM_TARGET_WILDCARD,
+ CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD,
+ channel, ROLE_UNKNOWN);
+ ahc_pause(ahc);
+
+ /* Make sure the sequencer is in a safe location. */
+ ahc_clear_critical_section(ahc);
+
+ /*
+ * Run our command complete fifos to ensure that we perform
+ * completion processing on any commands that 'completed'
+ * before the reset occurred.
+ */
+ ahc_run_qoutfifo(ahc);
+#if AHC_TARGET_MODE
+ /*
+ * XXX - In Twin mode, the tqinfifo may have commands
+ * for an unaffected channel in it. However, if
+ * we have run out of ATIO resources to drain that
+ * queue, we may not get them all out here. Further,
+ * the blocked transactions for the reset channel
+ * should just be killed off, irrespecitve of whether
+ * we are blocked on ATIO resources. Write a routine
+ * to compact the tqinfifo appropriately.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ ahc_run_tqinfifo(ahc, /*paused*/TRUE);
+ }
+#endif
+
+ /*
+ * Reset the bus if we are initiating this reset
+ */
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ cur_channel = 'A';
+ if ((ahc->features & AHC_TWIN) != 0
+ && ((sblkctl & SELBUSB) != 0))
+ cur_channel = 'B';
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ if (cur_channel != channel) {
+ /* Case 1: Command for another bus is active
+ * Stealthily reset the other bus without
+ * upsetting the current bus.
+ */
+ ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
+ simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#if AHC_TARGET_MODE
+ /*
+ * Bus resets clear ENSELI, so we cannot
+ * defer re-enabling bus reset interrupts
+ * if we are in target mode.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0)
+ simode1 |= ENSCSIRST;
+#endif
+ ahc_outb(ahc, SIMODE1, simode1);
+ if (initiate_reset)
+ ahc_reset_current_bus(ahc);
+ ahc_clear_intstat(ahc);
+ ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+ ahc_outb(ahc, SBLKCTL, sblkctl);
+ restart_needed = FALSE;
+ } else {
+ /* Case 2: A command from this bus is active or we're idle */
+ simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#if AHC_TARGET_MODE
+ /*
+ * Bus resets clear ENSELI, so we cannot
+ * defer re-enabling bus reset interrupts
+ * if we are in target mode.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0)
+ simode1 |= ENSCSIRST;
+#endif
+ ahc_outb(ahc, SIMODE1, simode1);
+ if (initiate_reset)
+ ahc_reset_current_bus(ahc);
+ ahc_clear_intstat(ahc);
+ ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+ restart_needed = TRUE;
+ }
+
+ /*
+ * Clean up all the state information for the
+ * pending transactions on this bus.
+ */
+ found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+ max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;
+
+#ifdef AHC_TARGET_MODE
+ /*
+ * Send an immediate notify ccb to all target more peripheral
+ * drivers affected by this action.
+ */
+ for (target = 0; target <= max_scsiid; target++) {
+ struct ahc_tmode_tstate* tstate;
+ u_int lun;
+
+ tstate = ahc->enabled_targets[target];
+ if (tstate == NULL)
+ continue;
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[lun];
+ if (lstate == NULL)
+ continue;
+
+ ahc_queue_lstate_event(ahc, lstate, CAM_TARGET_WILDCARD,
+ EVENT_TYPE_BUS_RESET, /*arg*/0);
+ ahc_send_lstate_events(ahc, lstate);
+ }
+ }
+#endif
+ /* Notify the XPT that a bus reset occurred */
+ ahc_send_async(ahc, devinfo.channel, CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
+
+ /*
+ * Revert to async/narrow transfers until we renegotiate.
+ */
+ for (target = 0; target <= max_scsiid; target++) {
+
+ if (ahc->enabled_targets[target] == NULL)
+ continue;
+ for (initiator = 0; initiator <= max_scsiid; initiator++) {
+ struct ahc_devinfo devinfo;
+
+ ahc_compile_devinfo(&devinfo, target, initiator,
+ CAM_LUN_WILDCARD,
+ channel, ROLE_UNKNOWN);
+ ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR, /*paused*/TRUE);
+ ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0,
+ /*ppr_options*/0, AHC_TRANS_CUR,
+ /*paused*/TRUE);
+ }
+ }
+
+ if (restart_needed)
+ ahc_restart(ahc);
+ else
+ ahc_unpause(ahc);
+ return found;
+}
+
+
+/***************************** Residual Processing ****************************/
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+void
+ahc_calc_residual(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ struct status_pkt *spkt;
+ uint32_t sgptr;
+ uint32_t resid_sgptr;
+ uint32_t resid =0;
+
+ /*
+ * 5 cases.
+ * 1) No residual.
+ * SG_RESID_VALID clear in sgptr.
+ * 2) Transferless command
+ * 3) Never performed any transfers.
+ * sgptr has SG_FULL_RESID set.
+ * 4) No residual but target did not
+ * save data pointers after the
+ * last transfer, so sgptr was
+ * never updated.
+ * 5) We have a partial residual.
+ * Use residual_sgptr to determine
+ * where we are.
+ */
+
+ hscb = scb->hscb;
+ sgptr = ahc_le32toh(hscb->sgptr);
+ if ((sgptr & SG_RESID_VALID) == 0)
+ /* Case 1 */
+ return;
+ sgptr &= ~SG_RESID_VALID;
+
+ if ((sgptr & SG_LIST_NULL) != 0)
+ /* Case 2 */
+ return;
+
+ spkt = &hscb->shared_data.status;
+ resid_sgptr = ahc_le32toh(spkt->residual_sg_ptr);
+ if ((sgptr & SG_FULL_RESID) != 0) {
+ /* Case 3 */
+ resid = ahc_get_transfer_length(scb);
+ } else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+ /* Case 4 */
+ return;
+ } else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+ panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+ } else {
+ struct ahc_dma_seg *sg;
+
+ /*
+ * Remainder of the SG where the transfer
+ * stopped.
+ */
+ resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;
+ sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ /*
+ * Add up the contents of all residual
+ * SG segments that are after the SG where
+ * the transfer stopped.
+ */
+ while ((ahc_le32toh(sg->len) & AHC_DMA_LAST_SEG) == 0) {
+ sg++;
+ resid += ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+ }
+ }
+ if ((scb->flags & SCB_SENSE) == 0)
+ ahc_set_residual(scb, resid);
+ else
+ ahc_set_sense_residual(scb, resid);
+
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOWMISC) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("Handled Residual of %d bytes\n", resid);
+ }
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHC_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahc_queue_lstate_event(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate,
+ u_int initiator_id, u_int event_type, u_int event_arg)
+{
+ struct ahc_tmode_event *event;
+ int pending;
+
+ xpt_freeze_devq(lstate->path, /*count*/1);
+ if (lstate->event_w_idx >= lstate->event_r_idx)
+ pending = lstate->event_w_idx - lstate->event_r_idx;
+ else
+ pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1
+ - (lstate->event_r_idx - lstate->event_w_idx);
+
+ if (event_type == EVENT_TYPE_BUS_RESET
+ || event_type == MSG_BUS_DEV_RESET) {
+ /*
+ * Any earlier events are irrelevant, so reset our buffer.
+ * This has the effect of allowing us to deal with reset
+ * floods (an external device holding down the reset line)
+ * without losing the event that is really interesting.
+ */
+ lstate->event_r_idx = 0;
+ lstate->event_w_idx = 0;
+ xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+ }
+
+ if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) {
+ xpt_print_path(lstate->path);
+ printf("immediate event %x:%x lost\n",
+ lstate->event_buffer[lstate->event_r_idx].event_type,
+ lstate->event_buffer[lstate->event_r_idx].event_arg);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+ }
+
+ event = &lstate->event_buffer[lstate->event_w_idx];
+ event->initiator_id = initiator_id;
+ event->event_type = event_type;
+ event->event_arg = event_arg;
+ lstate->event_w_idx++;
+ if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahc_send_lstate_events(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate)
+{
+ struct ccb_hdr *ccbh;
+ struct ccb_immed_notify *inot;
+
+ while (lstate->event_r_idx != lstate->event_w_idx
+ && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+ struct ahc_tmode_event *event;
+
+ event = &lstate->event_buffer[lstate->event_r_idx];
+ SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+ inot = (struct ccb_immed_notify *)ccbh;
+ switch (event->event_type) {
+ case EVENT_TYPE_BUS_RESET:
+ ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+ break;
+ default:
+ ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+ inot->message_args[0] = event->event_type;
+ inot->message_args[1] = event->event_arg;
+ break;
+ }
+ inot->initiator_id = event->initiator_id;
+ inot->sense_len = 0;
+ xpt_done((union ccb *)inot);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ }
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHC_DUMP_SEQ
+void
+ahc_dumpseq(struct ahc_softc* ahc)
+{
+ int i;
+ int max_prog;
+
+ if ((ahc->chip & AHC_BUS_MASK) < AHC_PCI)
+ max_prog = 448;
+ else if ((ahc->features & AHC_ULTRA2) != 0)
+ max_prog = 768;
+ else
+ max_prog = 512;
+
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+ for (i = 0; i < max_prog; i++) {
+ uint8_t ins_bytes[4];
+
+ ahc_insb(ahc, SEQRAM, ins_bytes, 4);
+ printf("0x%08x\n", ins_bytes[0] << 24
+ | ins_bytes[1] << 16
+ | ins_bytes[2] << 8
+ | ins_bytes[3]);
+ }
+}
+#endif
+
+static void
+ahc_loadseq(struct ahc_softc *ahc)
+{
+ struct cs cs_table[num_critical_sections];
+ u_int begin_set[num_critical_sections];
+ u_int end_set[num_critical_sections];
+ struct patch *cur_patch;
+ u_int cs_count;
+ u_int cur_cs;
+ u_int i;
+ int downloaded;
+ u_int skip_addr;
+ u_int sg_prefetch_cnt;
+ uint8_t download_consts[7];
+
+ /*
+ * Start out with 0 critical sections
+ * that apply to this firmware load.
+ */
+ cs_count = 0;
+ cur_cs = 0;
+ memset(begin_set, 0, sizeof(begin_set));
+ memset(end_set, 0, sizeof(end_set));
+
+ /* Setup downloadable constant table */
+ download_consts[QOUTFIFO_OFFSET] = 0;
+ if (ahc->targetcmds != NULL)
+ download_consts[QOUTFIFO_OFFSET] += 32;
+ download_consts[QINFIFO_OFFSET] = download_consts[QOUTFIFO_OFFSET] + 1;
+ download_consts[CACHESIZE_MASK] = ahc->pci_cachesize - 1;
+ download_consts[INVERTED_CACHESIZE_MASK] = ~(ahc->pci_cachesize - 1);
+ sg_prefetch_cnt = ahc->pci_cachesize;
+ if (sg_prefetch_cnt < (2 * sizeof(struct ahc_dma_seg)))
+ sg_prefetch_cnt = 2 * sizeof(struct ahc_dma_seg);
+ download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+ download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_cnt - 1);
+ download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_cnt - 1);
+
+ cur_patch = patches;
+ downloaded = 0;
+ skip_addr = 0;
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+
+ for (i = 0; i < sizeof(seqprog)/4; i++) {
+ if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
+ /*
+ * Don't download this instruction as it
+ * is in a patch that was removed.
+ */
+ continue;
+ }
+ /*
+ * Move through the CS table until we find a CS
+ * that might apply to this instruction.
+ */
+ for (; cur_cs < num_critical_sections; cur_cs++) {
+ if (critical_sections[cur_cs].end <= i) {
+ if (begin_set[cs_count] == TRUE
+ && end_set[cs_count] == FALSE) {
+ cs_table[cs_count].end = downloaded;
+ end_set[cs_count] = TRUE;
+ cs_count++;
+ }
+ continue;
+ }
+ if (critical_sections[cur_cs].begin <= i
+ && begin_set[cs_count] == FALSE) {
+ cs_table[cs_count].begin = downloaded;
+ begin_set[cs_count] = TRUE;
+ }
+ break;
+ }
+ ahc_download_instr(ahc, i, download_consts);
+ downloaded++;
+ }
+
+ ahc->num_critical_sections = cs_count;
+ if (cs_count != 0) {
+
+ cs_count *= sizeof(struct cs);
+ ahc->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
+ if (ahc->critical_sections == NULL)
+ panic("ahc_loadseq: Could not malloc");
+ memcpy(ahc->critical_sections, cs_table, cs_count);
+ }
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
+ ahc_restart(ahc);
+
+ if (bootverbose)
+ printf(" %d instructions downloaded\n", downloaded);
+}
+
+static int
+ahc_check_patch(struct ahc_softc *ahc, struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr)
+{
+ struct patch *cur_patch;
+ struct patch *last_patch;
+ u_int num_patches;
+
+ num_patches = sizeof(patches)/sizeof(struct patch);
+ last_patch = &patches[num_patches];
+ cur_patch = *start_patch;
+
+ while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+ if (cur_patch->patch_func(ahc) == 0) {
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ cur_patch += cur_patch->skip_patch;
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
+ */
+ cur_patch++;
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
+}
+
+static void
+ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
+{
+ union ins_formats instr;
+ struct ins_format1 *fmt1_ins;
+ struct ins_format3 *fmt3_ins;
+ u_int opcode;
+
+ /*
+ * The firmware is always compiled into a little endian format.
+ */
+ instr.integer = ahc_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+ fmt1_ins = &instr.format1;
+ fmt3_ins = NULL;
+
+ /* Pull the opcode */
+ opcode = instr.format1.opcode;
+ switch (opcode) {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ struct patch *cur_patch;
+ int address_offset;
+ u_int address;
+ u_int skip_addr;
+ u_int i;
+
+ fmt3_ins = &instr.format3;
+ address_offset = 0;
+ address = fmt3_ins->address;
+ cur_patch = patches;
+ skip_addr = 0;
+
+ for (i = 0; i < address;) {
+
+ ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
+
+ if (skip_addr > i) {
+ int end_addr;
+
+ end_addr = MIN(address, skip_addr);
+ address_offset += end_addr - i;
+ i = skip_addr;
+ } else {
+ i++;
+ }
+ }
+ address -= address_offset;
+ fmt3_ins->address = address;
+ /* FALLTHROUGH */
+ }
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_BMOV:
+ if (fmt1_ins->parity != 0) {
+ fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+ }
+ fmt1_ins->parity = 0;
+ if ((ahc->features & AHC_CMD_CHAN) == 0
+ && opcode == AIC_OP_BMOV) {
+ /*
+ * Block move was added at the same time
+ * as the command channel. Verify that
+ * this is only a move of a single element
+ * and convert the BMOV to a MOV
+ * (AND with an immediate of FF).
+ */
+ if (fmt1_ins->immediate != 1)
+ panic("%s: BMOV not supported\n",
+ ahc_name(ahc));
+ fmt1_ins->opcode = AIC_OP_AND;
+ fmt1_ins->immediate = 0xff;
+ }
+ /* FALLTHROUGH */
+ case AIC_OP_ROL:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ int i, count;
+
+ /* Calculate odd parity for the instruction */
+ for (i = 0, count = 0; i < 31; i++) {
+ uint32_t mask;
+
+ mask = 0x01 << i;
+ if ((instr.integer & mask) != 0)
+ count++;
+ }
+ if ((count & 0x01) == 0)
+ instr.format1.parity = 1;
+ } else {
+ /* Compress the instruction for older sequencers */
+ if (fmt3_ins != NULL) {
+ instr.integer =
+ fmt3_ins->immediate
+ | (fmt3_ins->source << 8)
+ | (fmt3_ins->address << 16)
+ | (fmt3_ins->opcode << 25);
+ } else {
+ instr.integer =
+ fmt1_ins->immediate
+ | (fmt1_ins->source << 8)
+ | (fmt1_ins->destination << 16)
+ | (fmt1_ins->ret << 24)
+ | (fmt1_ins->opcode << 25);
+ }
+ }
+ /* The sequencer is a little endian cpu */
+ instr.integer = ahc_htole32(instr.integer);
+ ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
+ break;
+ default:
+ panic("Unknown opcode encountered in seq program");
+ break;
+ }
+}
+
+void
+ahc_dump_card_state(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+ struct scb_tailq *untagged_q;
+ int target;
+ int maxtarget;
+ int i;
+ uint8_t last_phase;
+ uint8_t qinpos;
+ uint8_t qintail;
+ uint8_t qoutpos;
+ uint8_t scb_index;
+ uint8_t saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+
+ last_phase = ahc_inb(ahc, LASTPHASE);
+ printf("%s: Dumping Card State %s, at SEQADDR 0x%x\n",
+ ahc_name(ahc), ahc_lookup_phase_entry(last_phase)->phasemsg,
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+ printf("ACCUM = 0x%x, SINDEX = 0x%x, DINDEX = 0x%x, ARG_2 = 0x%x\n",
+ ahc_inb(ahc, ACCUM), ahc_inb(ahc, SINDEX), ahc_inb(ahc, DINDEX),
+ ahc_inb(ahc, ARG_2));
+ printf("HCNT = 0x%x SCBPTR = 0x%x\n", ahc_inb(ahc, HCNT),
+ ahc_inb(ahc, SCBPTR));
+ printf("SCSISEQ = 0x%x, SBLKCTL = 0x%x\n",
+ ahc_inb(ahc, SCSISEQ), ahc_inb(ahc, SBLKCTL));
+ printf(" DFCNTRL = 0x%x, DFSTATUS = 0x%x\n",
+ ahc_inb(ahc, DFCNTRL), ahc_inb(ahc, DFSTATUS));
+ printf("LASTPHASE = 0x%x, SCSISIGI = 0x%x, SXFRCTL0 = 0x%x\n",
+ last_phase, ahc_inb(ahc, SCSISIGI), ahc_inb(ahc, SXFRCTL0));
+ printf("SSTAT0 = 0x%x, SSTAT1 = 0x%x\n",
+ ahc_inb(ahc, SSTAT0), ahc_inb(ahc, SSTAT1));
+ if ((ahc->features & AHC_DT) != 0)
+ printf("SCSIPHASE = 0x%x\n", ahc_inb(ahc, SCSIPHASE));
+ printf("STACK == 0x%x, 0x%x, 0x%x, 0x%x\n",
+ ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
+ ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
+ ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
+ ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8));
+ printf("SCB count = %d\n", ahc->scb_data->numscbs);
+ printf("Kernel NEXTQSCB = %d\n", ahc->next_queued_scb->hscb->tag);
+ printf("Card NEXTQSCB = %d\n", ahc_inb(ahc, NEXT_QUEUED_SCB));
+ /* QINFIFO */
+ printf("QINFIFO entries: ");
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ qinpos = ahc_inb(ahc, SNSCB_QOFF);
+ ahc_outb(ahc, SNSCB_QOFF, qinpos);
+ } else
+ qinpos = ahc_inb(ahc, QINPOS);
+ qintail = ahc->qinfifonext;
+ while (qinpos != qintail) {
+ printf("%d ", ahc->qinfifo[qinpos]);
+ qinpos++;
+ }
+ printf("\n");
+
+ printf("Waiting Queue entries: ");
+ scb_index = ahc_inb(ahc, WAITING_SCBH);
+ i = 0;
+ while (scb_index != SCB_LIST_NULL && i++ < 256) {
+ ahc_outb(ahc, SCBPTR, scb_index);
+ printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+ scb_index = ahc_inb(ahc, SCB_NEXT);
+ }
+ printf("\n");
+
+ printf("Disconnected Queue entries: ");
+ scb_index = ahc_inb(ahc, DISCONNECTED_SCBH);
+ i = 0;
+ while (scb_index != SCB_LIST_NULL && i++ < 256) {
+ ahc_outb(ahc, SCBPTR, scb_index);
+ printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+ scb_index = ahc_inb(ahc, SCB_NEXT);
+ }
+ printf("\n");
+
+ ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+ printf("QOUTFIFO entries: ");
+ qoutpos = ahc->qoutfifonext;
+ i = 0;
+ while (ahc->qoutfifo[qoutpos] != SCB_LIST_NULL && i++ < 256) {
+ printf("%d ", ahc->qoutfifo[qoutpos]);
+ qoutpos++;
+ }
+ printf("\n");
+
+ printf("Sequencer Free SCB List: ");
+ scb_index = ahc_inb(ahc, FREE_SCBH);
+ i = 0;
+ while (scb_index != SCB_LIST_NULL && i++ < 256) {
+ ahc_outb(ahc, SCBPTR, scb_index);
+ printf("%d ", scb_index);
+ scb_index = ahc_inb(ahc, SCB_NEXT);
+ }
+ printf("\n");
+
+ printf("Sequencer SCB Info: ");
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ ahc_outb(ahc, SCBPTR, i);
+ printf("%d(c 0x%x, s 0x%x, l %d, t 0x%x) ",
+ i, ahc_inb(ahc, SCB_CONTROL),
+ ahc_inb(ahc, SCB_SCSIID),
+ ahc_inb(ahc, SCB_LUN),
+ ahc_inb(ahc, SCB_TAG));
+ }
+ printf("\n");
+
+ printf("Pending list: ");
+ i = 0;
+ LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+ if (i++ > 256)
+ break;
+ if (scb != LIST_FIRST(&ahc->pending_scbs))
+ printf(", ");
+ printf("%d(c 0x%x, s 0x%x, l %d)", scb->hscb->tag,
+ scb->hscb->control, scb->hscb->scsiid, scb->hscb->lun);
+ if ((ahc->flags & AHC_PAGESCBS) == 0) {
+ ahc_outb(ahc, SCBPTR, scb->hscb->tag);
+ printf("(0x%x, 0x%x)", ahc_inb(ahc, SCB_CONTROL),
+ ahc_inb(ahc, SCB_TAG));
+ }
+ }
+ printf("\n");
+
+ printf("Kernel Free SCB list: ");
+ i = 0;
+ SLIST_FOREACH(scb, &ahc->scb_data->free_scbs, links.sle) {
+ if (i++ > 256)
+ break;
+ printf("%d ", scb->hscb->tag);
+ }
+ printf("\n");
+
+ maxtarget = (ahc->features & (AHC_WIDE|AHC_TWIN)) ? 15 : 7;
+ for (target = 0; target <= maxtarget; target++) {
+ untagged_q = &ahc->untagged_queues[target];
+ if (TAILQ_FIRST(untagged_q) == NULL)
+ continue;
+ printf("Untagged Q(%d): ", target);
+ i = 0;
+ TAILQ_FOREACH(scb, untagged_q, links.tqe) {
+ if (i++ > 256)
+ break;
+ printf("%d ", scb->hscb->tag);
+ }
+ printf("\n");
+ }
+
+ ahc_platform_dump_card_state(ahc);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHC_TARGET_MODE
+cam_status
+ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
+ struct ahc_tmode_tstate **tstate,
+ struct ahc_tmode_lstate **lstate,
+ int notfound_failure)
+{
+
+ if ((ahc->features & AHC_TARGETMODE) == 0)
+ return (CAM_REQ_INVALID);
+
+ /*
+ * Handle the 'black hole' device that sucks up
+ * requests to unattached luns on enabled targets.
+ */
+ if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+ && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+ *tstate = NULL;
+ *lstate = ahc->black_hole;
+ } else {
+ u_int max_id;
+
+ max_id = (ahc->features & AHC_WIDE) ? 15 : 7;
+ if (ccb->ccb_h.target_id > max_id)
+ return (CAM_TID_INVALID);
+
+ if (ccb->ccb_h.target_lun >= AHC_NUM_LUNS)
+ return (CAM_LUN_INVALID);
+
+ *tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
+ *lstate = NULL;
+ if (*tstate != NULL)
+ *lstate =
+ (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+ }
+
+ if (notfound_failure != 0 && *lstate == NULL)
+ return (CAM_PATH_INVALID);
+
+ return (CAM_REQ_CMP);
+}
+
+void
+ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
+{
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_tmode_lstate *lstate;
+ struct ccb_en_lun *cel;
+ cam_status status;
+ u_int target;
+ u_int lun;
+ u_int target_mask;
+ u_long s;
+ char channel;
+
+ status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
+ /*notfound_failure*/FALSE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ if ((ahc->features & AHC_MULTIROLE) != 0) {
+ u_int our_id;
+
+ if (cam_sim_bus(sim) == 0)
+ our_id = ahc->our_id;
+ else
+ our_id = ahc->our_id_b;
+
+ if (ccb->ccb_h.target_id != our_id) {
+ if ((ahc->features & AHC_MULTI_TID) != 0
+ && (ahc->flags & AHC_INITIATORROLE) != 0) {
+ /*
+ * Only allow additional targets if
+ * the initiator role is disabled.
+ * The hardware cannot handle a re-select-in
+ * on the initiator id during a re-select-out
+ * on a different target id.
+ */
+ status = CAM_TID_INVALID;
+ } else if ((ahc->flags & AHC_INITIATORROLE) != 0
+ || ahc->enabled_luns > 0) {
+ /*
+ * Only allow our target id to change
+ * if the initiator role is not configured
+ * and there are no enabled luns which
+ * are attached to the currently registered
+ * scsi id.
+ */
+ status = CAM_TID_INVALID;
+ }
+ }
+ }
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ /*
+ * We now have an id that is valid.
+ * If we aren't in target mode, switch modes.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) == 0
+ && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+ u_long s;
+
+ printf("Configuring Target Mode\n");
+ ahc_lock(ahc, &s);
+ if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+ ccb->ccb_h.status = CAM_BUSY;
+ ahc_unlock(ahc, &s);
+ return;
+ }
+ ahc->flags |= AHC_TARGETROLE;
+ if ((ahc->features & AHC_MULTIROLE) == 0)
+ ahc->flags &= ~AHC_INITIATORROLE;
+ ahc_pause(ahc);
+ ahc_loadseq(ahc);
+ ahc_unlock(ahc, &s);
+ }
+ cel = &ccb->cel;
+ target = ccb->ccb_h.target_id;
+ lun = ccb->ccb_h.target_lun;
+ channel = SIM_CHANNEL(ahc, sim);
+ target_mask = 0x01 << target;
+ if (channel == 'B')
+ target_mask <<= 8;
+
+ if (cel->enable != 0) {
+ u_int scsiseq;
+
+ /* Are we already enabled?? */
+ if (lstate != NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun already enabled\n");
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ return;
+ }
+
+ if (cel->grp6_len != 0
+ || cel->grp7_len != 0) {
+ /*
+ * Don't (yet?) support vendor
+ * specific commands.
+ */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ printf("Non-zero Group Codes\n");
+ return;
+ }
+
+ /*
+ * Seems to be okay.
+ * Setup our data structures.
+ */
+ if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+ tstate = ahc_alloc_tstate(ahc, target, channel);
+ if (tstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate tstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ }
+ lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+ if (lstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate lstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ memset(lstate, 0, sizeof(*lstate));
+ status = xpt_create_path(&lstate->path, /*periph*/NULL,
+ xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ xpt_path_lun_id(ccb->ccb_h.path));
+ if (status != CAM_REQ_CMP) {
+ free(lstate, M_DEVBUF);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate path\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ SLIST_INIT(&lstate->accept_tios);
+ SLIST_INIT(&lstate->immed_notifies);
+ ahc_lock(ahc, &s);
+ ahc_pause(ahc);
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = lstate;
+ ahc->enabled_luns++;
+
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ u_int targid_mask;
+
+ targid_mask = ahc_inb(ahc, TARGID)
+ | (ahc_inb(ahc, TARGID + 1) << 8);
+
+ targid_mask |= target_mask;
+ ahc_outb(ahc, TARGID, targid_mask);
+ ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
+
+ ahc_update_scsiid(ahc, targid_mask);
+ } else {
+ u_int our_id;
+ char channel;
+
+ channel = SIM_CHANNEL(ahc, sim);
+ our_id = SIM_SCSI_ID(ahc, sim);
+
+ /*
+ * This can only happen if selections
+ * are not enabled
+ */
+ if (target != our_id) {
+ u_int sblkctl;
+ char cur_channel;
+ int swap;
+
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ cur_channel = (sblkctl & SELBUSB)
+ ? 'B' : 'A';
+ if ((ahc->features & AHC_TWIN) == 0)
+ cur_channel = 'A';
+ swap = cur_channel != channel;
+ if (channel == 'A')
+ ahc->our_id = target;
+ else
+ ahc->our_id_b = target;
+
+ if (swap)
+ ahc_outb(ahc, SBLKCTL,
+ sblkctl ^ SELBUSB);
+
+ ahc_outb(ahc, SCSIID, target);
+
+ if (swap)
+ ahc_outb(ahc, SBLKCTL, sblkctl);
+ }
+ }
+ } else
+ ahc->black_hole = lstate;
+ /* Allow select-in operations */
+ if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ scsiseq |= ENSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ scsiseq |= ENSELI;
+ ahc_outb(ahc, SCSISEQ, scsiseq);
+ }
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun now enabled for target mode\n");
+ } else {
+ struct scb *scb;
+ int i, empty;
+
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+
+ ahc_lock(ahc, &s);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+ struct ccb_hdr *ccbh;
+
+ ccbh = &scb->io_ctx->ccb_h;
+ if (ccbh->func_code == XPT_CONT_TARGET_IO
+ && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+ printf("CTIO pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ ahc_unlock(ahc, &s);
+ return;
+ }
+ }
+
+ if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+ printf("ATIOs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+ printf("INOTs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Target mode disabled\n");
+ xpt_free_path(lstate->path);
+ free(lstate, M_DEVBUF);
+
+ ahc_pause(ahc);
+ /* Can we clean up the target too? */
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = NULL;
+ ahc->enabled_luns--;
+ for (empty = 1, i = 0; i < 8; i++)
+ if (tstate->enabled_luns[i] != NULL) {
+ empty = 0;
+ break;
+ }
+
+ if (empty) {
+ ahc_free_tstate(ahc, target, channel,
+ /*force*/FALSE);
+ if (ahc->features & AHC_MULTI_TID) {
+ u_int targid_mask;
+
+ targid_mask = ahc_inb(ahc, TARGID)
+ | (ahc_inb(ahc, TARGID + 1)
+ << 8);
+
+ targid_mask &= ~target_mask;
+ ahc_outb(ahc, TARGID, targid_mask);
+ ahc_outb(ahc, TARGID+1,
+ (targid_mask >> 8));
+ ahc_update_scsiid(ahc, targid_mask);
+ }
+ }
+ } else {
+
+ ahc->black_hole = NULL;
+
+ /*
+ * We can't allow selections without
+ * our black hole device.
+ */
+ empty = TRUE;
+ }
+ if (ahc->enabled_luns == 0) {
+ /* Disallow select-in */
+ u_int scsiseq;
+
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ scsiseq &= ~ENSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ scsiseq &= ~ENSELI;
+ ahc_outb(ahc, SCSISEQ, scsiseq);
+
+ if ((ahc->features & AHC_MULTIROLE) == 0) {
+ printf("Configuring Initiator Mode\n");
+ ahc->flags &= ~AHC_TARGETROLE;
+ ahc->flags |= AHC_INITIATORROLE;
+ ahc_pause(ahc);
+ ahc_loadseq(ahc);
+ }
+ }
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ }
+}
+
+static void
+ahc_update_scsiid(struct ahc_softc *ahc, u_int targid_mask)
+{
+ u_int scsiid_mask;
+ u_int scsiid;
+
+ if ((ahc->features & AHC_MULTI_TID) == 0)
+ panic("ahc_update_scsiid called on non-multitid unit\n");
+
+ /*
+ * Since we will rely on the the TARGID mask
+ * for selection enables, ensure that OID
+ * in SCSIID is not set to some other ID
+ * that we don't want to allow selections on.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ scsiid = ahc_inb(ahc, SCSIID_ULTRA2);
+ else
+ scsiid = ahc_inb(ahc, SCSIID);
+ scsiid_mask = 0x1 << (scsiid & OID);
+ if ((targid_mask & scsiid_mask) == 0) {
+ u_int our_id;
+
+ /* ffs counts from 1 */
+ our_id = ffs(targid_mask);
+ if (our_id == 0)
+ our_id = ahc->our_id;
+ else
+ our_id--;
+ scsiid &= TID;
+ scsiid |= our_id;
+ }
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIID_ULTRA2, scsiid);
+ else
+ ahc_outb(ahc, SCSIID, scsiid);
+}
+
+void
+ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
+{
+ struct target_cmd *cmd;
+
+ /*
+ * If the card supports auto-access pause,
+ * we can access the card directly regardless
+ * of whether it is paused or not.
+ */
+ if ((ahc->features & AHC_AUTOPAUSE) != 0)
+ paused = TRUE;
+
+ ahc_sync_tqinfifo(ahc, BUS_DMASYNC_POSTREAD);
+ while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
+
+ /*
+ * Only advance through the queue if we
+ * have the resources to process the command.
+ */
+ if (ahc_handle_target_cmd(ahc, cmd) != 0)
+ break;
+
+ cmd->cmd_valid = 0;
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap,
+ ahc_targetcmd_offset(ahc, ahc->tqinfifonext),
+ sizeof(struct target_cmd),
+ BUS_DMASYNC_PREREAD);
+ ahc->tqinfifonext++;
+
+ /*
+ * Lazily update our position in the target mode incoming
+ * command queue as seen by the sequencer.
+ */
+ if ((ahc->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+ if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+ u_int hs_mailbox;
+
+ hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
+ hs_mailbox &= ~HOST_TQINPOS;
+ hs_mailbox |= ahc->tqinfifonext & HOST_TQINPOS;
+ ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
+ } else {
+ if (!paused)
+ ahc_pause(ahc);
+ ahc_outb(ahc, KERNEL_TQINPOS,
+ ahc->tqinfifonext & HOST_TQINPOS);
+ if (!paused)
+ ahc_unpause(ahc);
+ }
+ }
+ }
+}
+
+static int
+ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
+{
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_tmode_lstate *lstate;
+ struct ccb_accept_tio *atio;
+ uint8_t *byte;
+ int initiator;
+ int target;
+ int lun;
+
+ initiator = SCSIID_TARGET(ahc, cmd->scsiid);
+ target = SCSIID_OUR_ID(cmd->scsiid);
+ lun = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+ byte = cmd->bytes;
+ tstate = ahc->enabled_targets[target];
+ lstate = NULL;
+ if (tstate != NULL)
+ lstate = tstate->enabled_luns[lun];
+
+ /*
+ * Commands for disabled luns go to the black hole driver.
+ */
+ if (lstate == NULL)
+ lstate = ahc->black_hole;
+
+ atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+ if (atio == NULL) {
+ ahc->flags |= AHC_TQINFIFO_BLOCKED;
+ /*
+ * Wait for more ATIOs from the peripheral driver for this lun.
+ */
+ if (bootverbose)
+ printf("%s: ATIOs exhausted\n", ahc_name(ahc));
+ return (1);
+ } else
+ ahc->flags &= ~AHC_TQINFIFO_BLOCKED;
+#if 0
+ printf("Incoming command from %d for %d:%d%s\n",
+ initiator, target, lun,
+ lstate == ahc->black_hole ? "(Black Holed)" : "");
+#endif
+ SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+ if (lstate == ahc->black_hole) {
+ /* Fill in the wildcards */
+ atio->ccb_h.target_id = target;
+ atio->ccb_h.target_lun = lun;
+ }
+
+ /*
+ * Package it up and send it off to
+ * whomever has this lun enabled.
+ */
+ atio->sense_len = 0;
+ atio->init_id = initiator;
+ if (byte[0] != 0xFF) {
+ /* Tag was included */
+ atio->tag_action = *byte++;
+ atio->tag_id = *byte++;
+ atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+ } else {
+ atio->ccb_h.flags = 0;
+ }
+ byte++;
+
+ /* Okay. Now determine the cdb size based on the command code */
+ switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+ case 0:
+ atio->cdb_len = 6;
+ break;
+ case 1:
+ case 2:
+ atio->cdb_len = 10;
+ break;
+ case 4:
+ atio->cdb_len = 16;
+ break;
+ case 5:
+ atio->cdb_len = 12;
+ break;
+ case 3:
+ default:
+ /* Only copy the opcode. */
+ atio->cdb_len = 1;
+ printf("Reserved or VU command code type encountered\n");
+ break;
+ }
+
+ memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+ atio->ccb_h.status |= CAM_CDB_RECVD;
+
+ if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+ /*
+ * We weren't allowed to disconnect.
+ * We're hanging on the bus until a
+ * continue target I/O comes in response
+ * to this accept tio.
+ */
+#if 0
+ printf("Received Immediate Command %d:%d:%d - %p\n",
+ initiator, target, lun, ahc->pending_device);
+#endif
+ ahc->pending_device = lstate;
+ ahc_freeze_ccb((union ccb *)atio);
+ atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+ }
+ xpt_done((union ccb*)atio);
+ return (0);
+}
+
+#endif
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_host.h b/xen/drivers/scsi/aic7xxx/aic7xxx_host.h
new file mode 100644
index 0000000000..23710c6ed4
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_host.h
@@ -0,0 +1,103 @@
+/*
+ * Adaptec AIC7xxx device driver host template for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_host.h#9 $
+ */
+
+#ifndef _AIC7XXX_HOST_H_
+#define _AIC7XXX_HOST_H_
+
+#ifdef CONFIG_PROC_FS
+int ahc_linux_proc_info(char *, char **, off_t, int, int, int);
+#endif
+int ahc_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+int ahc_linux_detect(Scsi_Host_Template *);
+int ahc_linux_release(struct Scsi_Host *);
+const char *ahc_linux_info(struct Scsi_Host *);
+int ahc_linux_biosparam(Disk *, kdev_t, int[]);
+int ahc_linux_bus_reset(Scsi_Cmnd *);
+int ahc_linux_dev_reset(Scsi_Cmnd *);
+int ahc_linux_abort(Scsi_Cmnd *);
+
+#if defined(__i386__)
+# define AIC7XXX_BIOSPARAM ahc_linux_biosparam
+#else
+# define AIC7XXX_BIOSPARAM NULL
+#endif
+
+/*
+ * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields
+ * to do with card config are filled in after the card is detected.
+ */
+
+// proc_dir: NULL,
+ // proc_info: ahc_linux_proc_info,
+
+
+#define AIC7XXX { \
+ next: NULL, \
+ module: NULL, \
+ name: NULL, \
+ detect: ahc_linux_detect, \
+ release: ahc_linux_release, \
+ info: ahc_linux_info, \
+ command: NULL, \
+ queuecommand: ahc_linux_queue, \
+ eh_strategy_handler: NULL, \
+ eh_abort_handler: ahc_linux_abort, \
+ eh_device_reset_handler: ahc_linux_dev_reset, \
+ eh_bus_reset_handler: ahc_linux_bus_reset, \
+ eh_host_reset_handler: NULL, \
+ abort: NULL, \
+ reset: NULL, \
+ slave_attach: NULL, \
+ bios_param: AIC7XXX_BIOSPARAM, \
+ can_queue: AHC_MAX_QUEUE,/* max simultaneous cmds */\
+ this_id: -1, /* scsi id of host adapter */\
+ sg_tablesize: AHC_NSEG, /* max scatter-gather cmds */\
+ cmd_per_lun: 2, /* cmds per lun */\
+ present: 0, /* number of 7xxx's present */\
+ unchecked_isa_dma: 0, /* no memory DMA restrictions*/\
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 1 \
+}
+
+/* highmem_io: 1 \
+ */
+
+#endif /* _AIC7XXX_HOST_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_inline.h b/xen/drivers/scsi/aic7xxx/aic7xxx_inline.h
new file mode 100644
index 0000000000..2d774f2ab1
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_inline.h
@@ -0,0 +1,563 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_inline.h#35 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_inline.h,v 1.2.2.11 2002/04/29 19:36:31 gibbs Exp $
+ */
+
+#ifndef _AIC7XXX_INLINE_H_
+#define _AIC7XXX_INLINE_H_
+
+/************************* Sequencer Execution Control ************************/
+static __inline void ahc_pause_bug_fix(struct ahc_softc *ahc);
+static __inline int ahc_is_paused(struct ahc_softc *ahc);
+static __inline void ahc_pause(struct ahc_softc *ahc);
+static __inline void ahc_unpause(struct ahc_softc *ahc);
+
+/*
+ * Work around any chip bugs related to halting sequencer execution.
+ * On Ultra2 controllers, we must clear the CIOBUS stretch signal by
+ * reading a register that will set this signal and deassert it.
+ * Without this workaround, if the chip is paused, by an interrupt or
+ * manual pause while accessing scb ram, accesses to certain registers
+ * will hang the system (infinite pci retries).
+ */
+static __inline void
+ahc_pause_bug_fix(struct ahc_softc *ahc)
+{
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ (void)ahc_inb(ahc, CCSCBCTL);
+}
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+static __inline int
+ahc_is_paused(struct ahc_softc *ahc)
+{
+ return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop. The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register. The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+static __inline void
+ahc_pause(struct ahc_softc *ahc)
+{
+ ahc_outb(ahc, HCNTRL, ahc->pause);
+
+ /*
+ * Since the sequencer can disable pausing in a critical section, we
+ * must loop until it actually stops.
+ */
+ while (ahc_is_paused(ahc) == 0)
+ ;
+
+ ahc_pause_bug_fix(ahc);
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted. If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+static __inline void
+ahc_unpause(struct ahc_softc *ahc)
+{
+ if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
+ ahc_outb(ahc, HCNTRL, ahc->unpause);
+}
+
+/*********************** Untagged Transaction Routines ************************/
+static __inline void ahc_freeze_untagged_queues(struct ahc_softc *ahc);
+static __inline void ahc_release_untagged_queues(struct ahc_softc *ahc);
+
+/*
+ * Block our completion routine from starting the next untagged
+ * transaction for this target or target lun.
+ */
+static __inline void
+ahc_freeze_untagged_queues(struct ahc_softc *ahc)
+{
+ if ((ahc->flags & AHC_SCB_BTT) == 0)
+ ahc->untagged_queue_lock++;
+}
+
+/*
+ * Allow the next untagged transaction for this target or target lun
+ * to be executed. We use a counting semaphore to allow the lock
+ * to be acquired recursively. Once the count drops to zero, the
+ * transaction queues will be run.
+ */
+static __inline void
+ahc_release_untagged_queues(struct ahc_softc *ahc)
+{
+ if ((ahc->flags & AHC_SCB_BTT) == 0) {
+ ahc->untagged_queue_lock--;
+ if (ahc->untagged_queue_lock == 0)
+ ahc_run_untagged_queues(ahc);
+ }
+}
+
+/************************** Memory mapping routines ***************************/
+static __inline struct ahc_dma_seg *
+ ahc_sg_bus_to_virt(struct scb *scb,
+ uint32_t sg_busaddr);
+static __inline uint32_t
+ ahc_sg_virt_to_bus(struct scb *scb,
+ struct ahc_dma_seg *sg);
+static __inline uint32_t
+ ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index);
+static __inline void ahc_sync_scb(struct ahc_softc *ahc,
+ struct scb *scb, int op);
+static __inline void ahc_sync_sglist(struct ahc_softc *ahc,
+ struct scb *scb, int op);
+static __inline uint32_t
+ ahc_targetcmd_offset(struct ahc_softc *ahc,
+ u_int index);
+
+static __inline struct ahc_dma_seg *
+ahc_sg_bus_to_virt(struct scb *scb, uint32_t sg_busaddr)
+{
+ int sg_index;
+
+ sg_index = (sg_busaddr - scb->sg_list_phys)/sizeof(struct ahc_dma_seg);
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_index++;
+
+ return (&scb->sg_list[sg_index]);
+}
+
+static __inline uint32_t
+ahc_sg_virt_to_bus(struct scb *scb, struct ahc_dma_seg *sg)
+{
+ int sg_index;
+
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_index = sg - &scb->sg_list[1];
+
+ return (scb->sg_list_phys + (sg_index * sizeof(*scb->sg_list)));
+}
+
+static __inline uint32_t
+ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
+{
+ return (ahc->scb_data->hscb_busaddr
+ + (sizeof(struct hardware_scb) * index));
+}
+
+static __inline void
+ahc_sync_scb(struct ahc_softc *ahc, struct scb *scb, int op)
+{
+ ahc_dmamap_sync(ahc, ahc->scb_data->hscb_dmat,
+ ahc->scb_data->hscb_dmamap,
+ /*offset*/(scb->hscb - ahc->hscbs) * sizeof(*scb->hscb),
+ /*len*/sizeof(*scb->hscb), op);
+}
+
+static __inline void
+ahc_sync_sglist(struct ahc_softc *ahc, struct scb *scb, int op)
+{
+ if (scb->sg_count == 0)
+ return;
+
+ ahc_dmamap_sync(ahc, ahc->scb_data->sg_dmat, scb->sg_map->sg_dmamap,
+ /*offset*/(scb->sg_list - scb->sg_map->sg_vaddr)
+ * sizeof(struct ahc_dma_seg),
+ /*len*/sizeof(struct ahc_dma_seg) * scb->sg_count, op);
+}
+
+static __inline uint32_t
+ahc_targetcmd_offset(struct ahc_softc *ahc, u_int index)
+{
+ return (((uint8_t *)&ahc->targetcmds[index]) - ahc->qoutfifo);
+}
+
+/******************************** Debugging ***********************************/
+static __inline char *ahc_name(struct ahc_softc *ahc);
+
+static __inline char *
+ahc_name(struct ahc_softc *ahc)
+{
+ return (ahc->name);
+}
+
+/*********************** Miscelaneous Support Functions ***********************/
+
+static __inline void ahc_update_residual(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline struct ahc_initiator_tinfo *
+ ahc_fetch_transinfo(struct ahc_softc *ahc,
+ char channel, u_int our_id,
+ u_int remote_id,
+ struct ahc_tmode_tstate **tstate);
+static __inline struct scb*
+ ahc_get_scb(struct ahc_softc *ahc);
+static __inline void ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline void ahc_swap_with_next_hscb(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline void ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline struct scsi_sense_data *
+ ahc_get_sense_buf(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline uint32_t
+ ahc_get_sense_bufaddr(struct ahc_softc *ahc,
+ struct scb *scb);
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static __inline void
+ahc_update_residual(struct ahc_softc *ahc, struct scb *scb)
+{
+ uint32_t sgptr;
+
+ sgptr = ahc_le32toh(scb->hscb->sgptr);
+ if ((sgptr & SG_RESID_VALID) != 0)
+ ahc_calc_residual(ahc, scb);
+}
+
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+static __inline struct ahc_initiator_tinfo *
+ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
+ u_int remote_id, struct ahc_tmode_tstate **tstate)
+{
+ /*
+ * Transfer data structures are stored from the perspective
+ * of the target role. Since the parameters for a connection
+ * in the initiator role to a given target are the same as
+ * when the roles are reversed, we pretend we are the target.
+ */
+ if (channel == 'B')
+ our_id += 8;
+ *tstate = ahc->enabled_targets[our_id];
+ return (&(*tstate)->transinfo[remote_id]);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+static __inline struct scb *
+ahc_get_scb(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+
+ if ((scb = SLIST_FIRST(&ahc->scb_data->free_scbs)) == NULL) {
+ ahc_alloc_scbs(ahc);
+ scb = SLIST_FIRST(&ahc->scb_data->free_scbs);
+ if (scb == NULL)
+ return (NULL);
+ }
+ SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle);
+ return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+static __inline void
+ahc_free_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+
+ hscb = scb->hscb;
+ /* Clean up for the next user */
+ ahc->scb_data->scbindex[hscb->tag] = NULL;
+ scb->flags = SCB_FREE;
+ hscb->control = 0;
+
+ SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links.sle);
+
+ /* Notify the OSM that a resource is now available. */
+ ahc_platform_scb_free(ahc, scb);
+}
+
+static __inline struct scb *
+ahc_lookup_scb(struct ahc_softc *ahc, u_int tag)
+{
+ struct scb* scb;
+
+ scb = ahc->scb_data->scbindex[tag];
+ if (scb != NULL)
+ ahc_sync_scb(ahc, scb,
+ BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+ return (scb);
+}
+
+static __inline void
+ahc_swap_with_next_hscb(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct hardware_scb *q_hscb;
+ u_int saved_tag;
+
+ /*
+ * Our queuing method is a bit tricky. The card
+ * knows in advance which HSCB to download, and we
+ * can't disappoint it. To achieve this, the next
+ * SCB to download is saved off in ahc->next_queued_scb.
+ * When we are called to queue "an arbitrary scb",
+ * we copy the contents of the incoming HSCB to the one
+ * the sequencer knows about, swap HSCB pointers and
+ * finally assign the SCB to the tag indexed location
+ * in the scb_array. This makes sure that we can still
+ * locate the correct SCB by SCB_TAG.
+ */
+ q_hscb = ahc->next_queued_scb->hscb;
+ saved_tag = q_hscb->tag;
+ memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
+ if ((scb->flags & SCB_CDB32_PTR) != 0) {
+ q_hscb->shared_data.cdb_ptr =
+ ahc_htole32(ahc_hscb_busaddr(ahc, q_hscb->tag)
+ + offsetof(struct hardware_scb, cdb32));
+ }
+ q_hscb->tag = saved_tag;
+ q_hscb->next = scb->hscb->tag;
+
+ /* Now swap HSCB pointers. */
+ ahc->next_queued_scb->hscb = scb->hscb;
+ scb->hscb = q_hscb;
+
+ /* Now define the mapping from tag to SCB in the scbindex */
+ ahc->scb_data->scbindex[scb->hscb->tag] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+static __inline void
+ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ ahc_swap_with_next_hscb(ahc, scb);
+
+ if (scb->hscb->tag == SCB_LIST_NULL
+ || scb->hscb->next == SCB_LIST_NULL)
+ panic("Attempt to queue invalid SCB tag %x:%x\n",
+ scb->hscb->tag, scb->hscb->next);
+
+ /*
+ * Keep a history of SCBs we've downloaded in the qinfifo.
+ */
+ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+
+ /*
+ * Make sure our data is consistant from the
+ * perspective of the adapter.
+ */
+ ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+ /* Tell the adapter about the newly queued SCB */
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ if ((ahc->features & AHC_AUTOPAUSE) == 0)
+ ahc_pause(ahc);
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ if ((ahc->features & AHC_AUTOPAUSE) == 0)
+ ahc_unpause(ahc);
+ }
+}
+
+static __inline struct scsi_sense_data *
+ahc_get_sense_buf(struct ahc_softc *ahc, struct scb *scb)
+{
+ int offset;
+
+ offset = scb - ahc->scb_data->scbarray;
+ return (&ahc->scb_data->sense[offset]);
+}
+
+static __inline uint32_t
+ahc_get_sense_bufaddr(struct ahc_softc *ahc, struct scb *scb)
+{
+ int offset;
+
+ offset = scb - ahc->scb_data->scbarray;
+ return (ahc->scb_data->sense_busaddr
+ + (offset * sizeof(struct scsi_sense_data)));
+}
+
+/************************** Interrupt Processing ******************************/
+static __inline void ahc_sync_qoutfifo(struct ahc_softc *ahc, int op);
+static __inline void ahc_sync_tqinfifo(struct ahc_softc *ahc, int op);
+static __inline u_int ahc_check_cmdcmpltqueues(struct ahc_softc *ahc);
+static __inline void ahc_intr(struct ahc_softc *ahc);
+
+static __inline void
+ahc_sync_qoutfifo(struct ahc_softc *ahc, int op)
+{
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+ /*offset*/0, /*len*/256, op);
+}
+
+static __inline void
+ahc_sync_tqinfifo(struct ahc_softc *ahc, int op)
+{
+#ifdef AHC_TARGET_MODE
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap,
+ ahc_targetcmd_offset(ahc, 0),
+ sizeof(struct target_cmd) * AHC_TMODE_CMDS,
+ op);
+ }
+#endif
+}
+
+/*
+ * See if the firmware has posted any completed commands
+ * into our in-core command complete fifos.
+ */
+#define AHC_RUN_QOUTFIFO 0x1
+#define AHC_RUN_TQINFIFO 0x2
+static __inline u_int
+ahc_check_cmdcmpltqueues(struct ahc_softc *ahc)
+{
+ u_int retval;
+
+ retval = 0;
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+ /*offset*/ahc->qoutfifonext, /*len*/1,
+ BUS_DMASYNC_POSTREAD);
+ if (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL)
+ retval |= AHC_RUN_QOUTFIFO;
+#ifdef AHC_TARGET_MODE
+ if ((ahc->flags & AHC_TARGETROLE) != 0
+ && (ahc->flags & AHC_TQINFIFO_BLOCKED) == 0) {
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap,
+ ahc_targetcmd_offset(ahc, ahc->tqinfifofnext),
+ /*len*/sizeof(struct target_cmd),
+ BUS_DMASYNC_POSTREAD);
+ if (ahc->targetcmds[ahc->tqinfifonext].cmd_valid != 0)
+ retval |= AHC_RUN_TQINFIFO;
+ }
+#endif
+ return (retval);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+static __inline void
+ahc_intr(struct ahc_softc *ahc)
+{
+ u_int intstat;
+
+ /*
+ * Instead of directly reading the interrupt status register,
+ * infer the cause of the interrupt by checking our in-core
+ * completion queues. This avoids a costly PCI bus read in
+ * most cases.
+ */
+ if ((ahc->flags & (AHC_ALL_INTERRUPTS|AHC_EDGE_INTERRUPT)) == 0
+ && (ahc_check_cmdcmpltqueues(ahc) != 0))
+ intstat = CMDCMPLT;
+ else {
+ intstat = ahc_inb(ahc, INTSTAT);
+ }
+
+ if (intstat & CMDCMPLT) {
+ ahc_outb(ahc, CLRINT, CLRCMDINT);
+
+ /*
+ * Ensure that the chip sees that we've cleared
+ * this interrupt before we walk the output fifo.
+ * Otherwise, we may, due to posted bus writes,
+ * clear the interrupt after we finish the scan,
+ * and after the sequencer has added new entries
+ * and asserted the interrupt again.
+ */
+ ahc_flush_device_writes(ahc);
+ ahc_run_qoutfifo(ahc);
+#ifdef AHC_TARGET_MODE
+ if ((ahc->flags & AHC_TARGETROLE) != 0)
+ ahc_run_tqinfifo(ahc, /*paused*/FALSE);
+#endif
+ }
+
+ if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
+ /* Hot eject */
+ return;
+
+ if ((intstat & INT_PEND) == 0) {
+#if AHC_PCI_CONFIG > 0
+ if (ahc->unsolicited_ints > 500) {
+ ahc->unsolicited_ints = 0;
+ if ((ahc->chip & AHC_PCI) != 0
+ && (ahc_inb(ahc, ERROR) & PCIERRSTAT) != 0)
+ ahc->bus_intr(ahc);
+ }
+#endif
+ ahc->unsolicited_ints++;
+ return;
+ }
+ ahc->unsolicited_ints = 0;
+
+ if (intstat & BRKADRINT) {
+ ahc_handle_brkadrint(ahc);
+ /* Fatal error, no more interrupts to handle. */
+ return;
+ }
+
+ if ((intstat & (SEQINT|SCSIINT)) != 0)
+ ahc_pause_bug_fix(ahc);
+
+ if ((intstat & SEQINT) != 0)
+ ahc_handle_seqint(ahc, intstat);
+
+ if ((intstat & SCSIINT) != 0)
+ ahc_handle_scsiint(ahc, intstat);
+}
+
+#endif /* _AIC7XXX_INLINE_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_osm.c b/xen/drivers/scsi/aic7xxx/aic7xxx_osm.c
new file mode 100644
index 0000000000..e868f32bbf
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -0,0 +1,3071 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.c#103 $
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * 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, 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
+ * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
+ * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
+ * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
+ * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
+ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
+ * ANSI SCSI-2 specification (draft 10c), ...
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ * Substantially modified to include support for wide and twin bus
+ * adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * SCB paging, and other rework of the code.
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-2000 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * Thanks also go to (in alphabetical order) the following:
+ *
+ * Rory Bolt - Sequencer bug fixes
+ * Jay Estabrook - Initial DEC Alpha support
+ * Doug Ledford - Much needed abort/reset bug fixes
+ * Kai Makisara - DMAing of SCBs
+ *
+ * A Boot time option was also added for not resetting the scsi bus.
+ *
+ * Form: aic7xxx=extended
+ * aic7xxx=no_reset
+ * aic7xxx=verbose
+ *
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
+ *
+ * Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp
+ */
+
+/*
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
+ *
+ * Copyright (c) 1997-1999 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs. Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ * 1: Import of the latest FreeBSD sequencer code for this driver
+ * 2: Modification of kernel code to accomodate different sequencer semantics
+ * 3: Extensive changes throughout kernel portion of driver to improve
+ * abort/reset processing and error hanndling
+ * 4: Other work contributed by various people on the Internet
+ * 5: Changes to printk information and verbosity selection code
+ * 6: General reliability related changes, especially in IRQ management
+ * 7: Modifications to the default probe/attach order for supported cards
+ * 8: SMP friendliness has been improved
+ *
+ */
+
+/*
+ * This is the only file where module.h should
+ * embed module global version info.
+ */
+//#define AHC_MODVERSION_FILE
+
+#include <xeno/lib.h>
+#include <xeno/string.h>
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+#include <xeno/init.h> /* __setup */
+#endif
+
+#include "../sd.h" /* For geometry detection */
+
+#include <xeno/mm.h> /* For fetching system memory size */
+#include <xeno/blk.h> /* For block_size() */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+/*
+ * Lock protecting manipulation of the ahc softc list.
+ */
+spinlock_t ahc_list_spinlock;
+#endif
+
+/*
+ * To generate the correct addresses for the controller to issue
+ * on the bus. Originally added for DEC Alpha support.
+ */
+#define VIRT_TO_BUS(a) (uint32_t)virt_to_bus((void *)(a))
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+struct proc_dir_entry proc_scsi_aic7xxx = {
+ PROC_SCSI_AIC7XXX, 7, "aic7xxx",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2,
+ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+/*
+ * Set this to the delay in seconds after SCSI bus reset.
+ * Note, we honor this only for the initial bus reset.
+ * The scsi error recovery code performs its own bus settle
+ * delay handling for error recovery actions.
+ */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY_MS
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY_MS
+#else
+#define AIC7XXX_RESET_DELAY 500
+#endif
+
+/*
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
+ *
+ * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
+ * NOTE: This does affect performance since it has to maintain statistics.
+ */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
+
+/*
+ * To change the default number of tagged transactions allowed per-device,
+ * add a line to the lilo.conf file like:
+ * append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * The tag_commands is an array of 16 to allow for wide and twin adapters.
+ * Twin adapters will use indexes 0-7 for channel 0, and indexes 8-15
+ * for channel 1.
+ */
+typedef struct {
+ uint8_t tag_commands[16]; /* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Modify this as you see fit for your system.
+ *
+ * 0 tagged queuing disabled
+ * 1 <= n <= 253 n == max tags ever dispatched.
+ *
+ * The driver will throttle the number of commands dispatched to a
+ * device if it returns queue full. For devices with a fixed maximum
+ * queue depth, the driver will eventually determine this depth and
+ * lock it in (a console message is printed to indicate that a lock
+ * has occurred). On some devices, queue full is returned for a temporary
+ * resource shortage. These devices will return queue full at varying
+ * depths. The driver will throttle back when the queue fulls occur and
+ * attempt to slowly increase the depth over time as the device recovers
+ * from the resource shortage.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to attempt to use up to 64 tags for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3. It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ * to modify in order to change things is just below this comment block.
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}},
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#else
+#define AIC7XXX_CMDS_PER_DEVICE AHC_MAX_QUEUE
+#endif
+
+#define AIC7XXX_CONFIGED_TAG_COMMANDS { \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE \
+}
+
+/*
+ * By default, use the number of commands specified by
+ * the users kernel configuration.
+ */
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS}
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW DID_ERROR
+
+void
+ahc_print_path(struct ahc_softc *ahc, struct scb *scb)
+{
+ printf("(scsi%d:%c:%d:%d): ",
+ ahc->platform_data->host->host_no,
+ scb != NULL ? SCB_GET_CHANNEL(ahc, scb) : 'X',
+ scb != NULL ? SCB_GET_TARGET(ahc, scb) : -1,
+ scb != NULL ? SCB_GET_LUN(scb) : -1);
+}
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ * cards in the system. This should be fixed. Exceptions to this
+ * rule are noted in the comments.
+ */
+
+/*
+ * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static uint32_t aic7xxx_no_reset;
+
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips. The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect. Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from. The only exceptions to this are when a controller
+ * has its BIOS disabled. So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number. We also force
+ * all controllers with their BIOS disabled to the end of the list. This
+ * works on *almost* all computers. Where it doesn't work, we have this
+ * option. Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end. That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic7xxx_reverse_scan = 0;
+
+/*
+ * Should we force EXTENDED translation on a controller.
+ * 0 == Use whatever is in the SEEPROM or default to off
+ * 1 == Use whatever is in the SEEPROM or default to on
+ */
+static uint32_t aic7xxx_extended = 0;
+
+/*
+ * PCI bus parity checking of the Adaptec controllers. This is somewhat
+ * dubious at best. To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ * 0 = Shut off PCI parity check
+ * -1 = Normal polarity pci parity checking
+ * 1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt. So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number. That will invert the 0 which will result in
+ * -1.
+ */
+static int aic7xxx_pci_parity = 0;
+
+/*
+ * Certain newer motherboards have put new PCI based devices into the
+ * IO spaces that used to typically be occupied by VLB or EISA cards.
+ * This overlap can cause these newer motherboards to lock up when scanned
+ * for older EISA and VLB devices. Setting this option to non-0 will
+ * cause the driver to skip scanning for any VLB or EISA controllers and
+ * only support the PCI controllers. NOTE: this means that if the kernel
+ * os compiled with PCI support disabled, then setting this to non-0
+ * would result in never finding any devices :)
+ */
+#ifndef CONFIG_AIC7XXX_PROBE_EISA_VL
+#define CONFIG_AIC7XXX_PROBE_EISA_VL n
+#endif
+#if CONFIG_AIC7XXX_PROBE_EISA_VL == n
+static int aic7xxx_no_probe = 1;
+#else
+static int aic7xxx_no_probe;
+#endif
+
+/*
+ * aic7xxx_detect() has been run, so register all device arrivals
+ * immediately with the system rather than deferring to the sorted
+ * attachment performed by aic7xxx_detect().
+ */
+int aic7xxx_detect_complete;
+
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ * 0 - 256ms
+ * 1 - 128ms
+ * 2 - 64ms
+ * 3 - 32ms
+ * We default to 256ms because some older devices need a longer time
+ * to respond to initial selection.
+ */
+static int aic7xxx_seltime = 0x00;
+
+/*
+ * Certain devices do not perform any aging on commands. Should the
+ * device be saturated by commands in one portion of the disk, it is
+ * possible for transactions on far away sectors to never be serviced.
+ * To handle these devices, we can periodically send an ordered tag to
+ * force all outstanding transactions to be serviced prior to a new
+ * transaction.
+ */
+int aic7xxx_periodic_otag;
+
+/*
+ * Module information and settable options.
+ */
+#ifdef MODULE
+static char *aic7xxx = NULL;
+/*
+ * Just in case someone uses commas to separate items on the insmod
+ * command line, we define a dummy buffer here to avoid having insmod
+ * write wild stuff into our code segment
+ */
+static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
+/*
+MODULE_AUTHOR("Maintainer: Justin T. Gibbs <gibbs@scsiguy.com>");
+MODULE_DESCRIPTION("Adaptec Aic77XX/78XX SCSI Host Bus Adapter driver");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+MODULE_PARM(aic7xxx, "s");
+MODULE_PARM_DESC(aic7xxx, "period delimited, options string.
+ verbose Enable verbose/diagnostic logging
+ no_probe Disable EISA/VLB controller probing
+ no_reset Supress initial bus resets
+ extended Enable extended geometry on all controllers
+ periodic_otag Send an ordered tagged transaction periodically
+ to prevent tag starvation. This may be
+ required by some older disk drives/RAID arrays.
+ reverse_scan Sort PCI devices highest Bus/Slot to lowest
+ tag_info:<tag_str> Set per-target tag depth
+ seltime:<int> Selection Timeout(0/256ms,1/128ms,2/64ms,3/32ms)
+
+ Sample /etc/modules.conf line:
+ Enable verbose logging
+ Disable EISA/VLB probing
+ Set tag depth on Controller 2/Target 2 to 10 tags
+ Shorten the selection timeout to 128ms from its default of 256
+
+ options aic7xxx='\"verbose.no_probe.tag_info:{{}.{}.{..10}}.seltime:1\"'
+");
+*/
+#endif
+
+static void ahc_linux_handle_scsi_status(struct ahc_softc *,
+ struct ahc_linux_device *,
+ struct scb *);
+static void ahc_linux_filter_command(struct ahc_softc*, Scsi_Cmnd*,
+ struct scb*);
+//static void ahc_linux_sem_timeout(u_long arg);
+static void ahc_linux_freeze_sim_queue(struct ahc_softc *ahc);
+static void ahc_linux_release_sim_queue(u_long arg);
+static void ahc_linux_dev_timed_unfreeze(u_long arg);
+static int ahc_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag);
+static void ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc);
+static void ahc_linux_select_queue_depth(struct Scsi_Host *host,
+ Scsi_Device *scsi_devs);
+static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_linux_device_queue_depth(struct ahc_softc *ahc,
+ Scsi_Device *device);
+static struct ahc_linux_target* ahc_linux_alloc_target(struct ahc_softc*,
+ u_int, u_int);
+static void ahc_linux_free_target(struct ahc_softc*,
+ struct ahc_linux_target*);
+static struct ahc_linux_device* ahc_linux_alloc_device(struct ahc_softc*,
+ struct ahc_linux_target*,
+ u_int);
+static void ahc_linux_free_device(struct ahc_softc*,
+ struct ahc_linux_device*);
+static void ahc_linux_run_device_queue(struct ahc_softc*,
+ struct ahc_linux_device*);
+static void ahc_linux_setup_tag_info(char *p, char *end);
+static int ahc_linux_next_unit(void);
+static void ahc_runq_tasklet(unsigned long data);
+static int ahc_linux_halt(struct notifier_block *nb, u_long event, void *buf);
+
+static __inline struct ahc_linux_device*
+ ahc_linux_get_device(struct ahc_softc *ahc, u_int channel,
+ u_int target, u_int lun, int alloc);
+static __inline void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc,
+ Scsi_Cmnd *cmd);
+static __inline void ahc_linux_run_complete_queue(struct ahc_softc *ahc,
+ struct ahc_cmd *acmd);
+static __inline void ahc_linux_check_device_queue(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev);
+static __inline struct ahc_linux_device *
+ ahc_linux_next_device_to_run(struct ahc_softc *ahc);
+static __inline void ahc_linux_run_device_queues(struct ahc_softc *ahc);
+static __inline void ahc_linux_sniff_command(struct ahc_softc*, Scsi_Cmnd*,
+ struct scb*);
+static __inline void ahc_linux_unmap_scb(struct ahc_softc*, struct scb*);
+
+static __inline int ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+ struct ahc_dma_seg *sg,
+ bus_addr_t addr, bus_size_t len);
+
+static __inline struct ahc_linux_device*
+ahc_linux_get_device(struct ahc_softc *ahc, u_int channel, u_int target,
+ u_int lun, int alloc)
+{
+ struct ahc_linux_target *targ;
+ struct ahc_linux_device *dev;
+ u_int target_offset;
+
+ target_offset = target;
+ if (channel != 0)
+ target_offset += 8;
+ targ = ahc->platform_data->targets[target_offset];
+ if (targ == NULL) {
+ if (alloc != 0) {
+ targ = ahc_linux_alloc_target(ahc, channel, target);
+ if (targ == NULL)
+ return (NULL);
+ } else
+ return (NULL);
+ }
+ dev = targ->devices[lun];
+ if (dev == NULL && alloc != 0)
+ dev = ahc_linux_alloc_device(ahc, targ, lun);
+ return (dev);
+}
+
+static __inline void
+ahc_linux_queue_cmd_complete(struct ahc_softc *ahc, Scsi_Cmnd *cmd)
+{
+ /*
+ * Typically, the complete queue has very few entries
+ * queued to it before the queue is emptied by
+ * ahc_linux_run_complete_queue, so sorting the entries
+ * by generation number should be inexpensive.
+ * We perform the sort so that commands that complete
+ * with an error are retuned in the order origionally
+ * queued to the controller so that any subsequent retries
+ * are performed in order. The underlying ahc routines do
+ * not guarantee the order that aborted commands will be
+ * returned to us.
+ */
+ struct ahc_completeq *completeq;
+ struct ahc_cmd *list_cmd;
+ struct ahc_cmd *acmd;
+
+ /*
+ * If we want the request requeued, make sure there
+ * are sufficent retries. In the old scsi error code,
+ * we used to be able to specify a result code that
+ * bypassed the retry count. Now we must use this
+ * hack.
+ */
+ if (cmd->result == (CAM_REQUEUE_REQ << 16))
+ cmd->retries--;
+ completeq = &ahc->platform_data->completeq;
+ list_cmd = TAILQ_FIRST(completeq);
+ acmd = (struct ahc_cmd *)cmd;
+ while (list_cmd != NULL
+ && acmd_scsi_cmd(list_cmd).serial_number
+ < acmd_scsi_cmd(acmd).serial_number)
+ list_cmd = TAILQ_NEXT(list_cmd, acmd_links.tqe);
+ if (list_cmd != NULL)
+ TAILQ_INSERT_BEFORE(list_cmd, acmd, acmd_links.tqe);
+ else
+ TAILQ_INSERT_TAIL(completeq, acmd, acmd_links.tqe);
+}
+
+static __inline void
+ahc_linux_run_complete_queue(struct ahc_softc *ahc, struct ahc_cmd *acmd)
+{
+ u_long done_flags;
+
+ ahc_done_lock(ahc, &done_flags);
+ while (acmd != NULL) {
+ Scsi_Cmnd *cmd;
+
+ cmd = &acmd_scsi_cmd(acmd);
+ acmd = TAILQ_NEXT(acmd, acmd_links.tqe);
+ cmd->host_scribble = NULL;
+ cmd->scsi_done(cmd);
+ }
+ ahc_done_unlock(ahc, &done_flags);
+}
+
+static __inline void
+ahc_linux_check_device_queue(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev)
+{
+ if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) != 0
+ && dev->active == 0) {
+ dev->flags &= ~AHC_DEV_FREEZE_TIL_EMPTY;
+ dev->qfrozen--;
+ }
+
+ if (TAILQ_FIRST(&dev->busyq) == NULL
+ || dev->openings == 0 || dev->qfrozen != 0)
+ return;
+
+ ahc_linux_run_device_queue(ahc, dev);
+}
+
+static __inline struct ahc_linux_device *
+ahc_linux_next_device_to_run(struct ahc_softc *ahc)
+{
+
+ if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0
+ || ahc->platform_data->qfrozen != 0)
+ return (NULL);
+ return (TAILQ_FIRST(&ahc->platform_data->device_runq));
+}
+
+static __inline void
+ahc_linux_run_device_queues(struct ahc_softc *ahc)
+{
+ struct ahc_linux_device *dev;
+
+ while ((dev = ahc_linux_next_device_to_run(ahc)) != NULL) {
+ TAILQ_REMOVE(&ahc->platform_data->device_runq, dev, links);
+ dev->flags &= ~AHC_DEV_ON_RUN_LIST;
+ ahc_linux_check_device_queue(ahc, dev);
+ }
+}
+
+static __inline void
+ahc_linux_sniff_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd, struct scb *scb)
+{
+ /*
+ * Determine whether we care to filter
+ * information out of this command. If so,
+ * pass it on to ahc_linux_filter_command() for more
+ * heavy weight processing.
+ */
+ if (cmd->cmnd[0] == INQUIRY)
+ ahc_linux_filter_command(ahc, cmd, scb);
+}
+
+static __inline void
+ahc_linux_unmap_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ Scsi_Cmnd *cmd;
+
+ cmd = scb->io_ctx;
+ ahc_sync_sglist(ahc, scb, BUS_DMASYNC_POSTWRITE);
+ if (cmd->use_sg != 0) {
+ struct scatterlist *sg;
+
+ sg = (struct scatterlist *)cmd->request_buffer;
+ pci_unmap_sg(ahc->dev_softc, sg, cmd->use_sg,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ } else if (cmd->request_bufflen != 0) {
+ pci_unmap_single(ahc->dev_softc,
+ scb->platform_data->buf_busaddr,
+ cmd->request_bufflen,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ }
+}
+
+static __inline int
+ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+ struct ahc_dma_seg *sg, bus_addr_t addr, bus_size_t len)
+{
+ int consumed;
+
+ if ((scb->sg_count + 1) > AHC_NSEG)
+ panic("Too few segs for dma mapping. "
+ "Increase AHC_NSEG\n");
+
+ consumed = 1;
+ sg->addr = ahc_htole32(addr & 0xFFFFFFFF);
+ scb->platform_data->xfer_len += len;
+ if (sizeof(bus_addr_t) > 4
+ && (ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ /*
+ * Due to DAC restrictions, we can't
+ * cross a 4GB boundary.
+ */
+ if ((addr ^ (addr + len - 1)) & ~0xFFFFFFFF) {
+ struct ahc_dma_seg *next_sg;
+ uint32_t next_len;
+
+ printf("Crossed Seg\n");
+ if ((scb->sg_count + 2) > AHC_NSEG)
+ panic("Too few segs for dma mapping. "
+ "Increase AHC_NSEG\n");
+
+ consumed++;
+ next_sg = sg + 1;
+ next_sg->addr = 0;
+ next_len = 0x100000000 - (addr & 0xFFFFFFFF);
+ len -= next_len;
+ next_len |= ((addr >> 8) + 0x1000000) & 0x7F000000;
+ next_sg->len = ahc_htole32(next_len);
+ }
+ len |= (addr >> 8) & 0x7F000000;
+ }
+ sg->len = ahc_htole32(len);
+ return (consumed);
+}
+
+/**************************** Tasklet Handler *********************************/
+
+static void
+ahc_runq_tasklet(unsigned long data)
+{
+ struct ahc_softc* ahc;
+ struct ahc_linux_device *dev;
+ u_long flags;
+
+ ahc = (struct ahc_softc *)data;
+ ahc_lock(ahc, &flags);
+ while ((dev = ahc_linux_next_device_to_run(ahc)) != NULL) {
+
+ TAILQ_REMOVE(&ahc->platform_data->device_runq, dev, links);
+ dev->flags &= ~AHC_DEV_ON_RUN_LIST;
+ ahc_linux_check_device_queue(ahc, dev);
+ /* Yeild to our interrupt handler */
+ ahc_unlock(ahc, &flags);
+ ahc_lock(ahc, &flags);
+ }
+ ahc_unlock(ahc, &flags);
+}
+
+/************************ Shutdown/halt/reboot hook ***************************/
+#include <xeno/notifier.h>
+#include <xeno/reboot.h>
+
+#if XENO_KILLED
+static struct notifier_block ahc_linux_notifier = {
+ ahc_linux_halt, NULL, 0
+};
+#endif
+
+static int ahc_linux_halt(struct notifier_block *nb, u_long event, void *buf)
+{
+ struct ahc_softc *ahc;
+
+ if (event == SYS_DOWN || event == SYS_HALT) {
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+ ahc_shutdown(ahc);
+ }
+ }
+ return (NOTIFY_OK);
+}
+
+/******************************** Macros **************************************/
+#define BUILD_SCSIID(ahc, cmd) \
+ ((((cmd)->target << TID_SHIFT) & TID) \
+ | (((cmd)->channel == 0) ? (ahc)->our_id : (ahc)->our_id_b) \
+ | (((cmd)->channel == 0) ? 0 : TWIN_CHNLB))
+
+/******************************** Bus DMA *************************************/
+int
+ahc_dma_tag_create(struct ahc_softc *ahc, bus_dma_tag_t parent,
+ bus_size_t alignment, bus_size_t boundary,
+ bus_addr_t lowaddr, bus_addr_t highaddr,
+ bus_dma_filter_t *filter, void *filterarg,
+ bus_size_t maxsize, int nsegments,
+ bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag)
+{
+ bus_dma_tag_t dmat;
+
+ dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT);
+ if (dmat == NULL)
+ return (ENOMEM);
+
+ /*
+ * Linux is very simplistic about DMA memory. For now don't
+ * maintain all specification information. Once Linux supplies
+ * better facilities for doing these operations, or the
+ * needs of this particular driver change, we might need to do
+ * more here.
+ */
+ dmat->alignment = alignment;
+ dmat->boundary = boundary;
+ dmat->maxsize = maxsize;
+ *ret_tag = dmat;
+ return (0);
+}
+
+void
+ahc_dma_tag_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat)
+{
+ free(dmat, M_DEVBUF);
+}
+
+int
+ahc_dmamem_alloc(struct ahc_softc *ahc, bus_dma_tag_t dmat, void** vaddr,
+ int flags, bus_dmamap_t *mapp)
+{
+ bus_dmamap_t map;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT);
+ if (map == NULL)
+ return (ENOMEM);
+ /*
+ * Although we can dma data above 4GB, our
+ * "consistent" memory is below 4GB for
+ * space efficiency reasons (only need a 4byte
+ * address). For this reason, we have to reset
+ * our dma mask when doing allocations.
+ */
+ if (ahc->dev_softc != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+ pci_set_dma_mask(ahc->dev_softc, 0xFFFFFFFF);
+#else
+ ahc->dev_softc->dma_mask = 0xFFFFFFFF;
+#endif
+ }
+ *vaddr = pci_alloc_consistent(ahc->dev_softc,
+ dmat->maxsize, &map->bus_addr);
+ if (ahc->dev_softc != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+ pci_set_dma_mask(ahc->dev_softc,
+ ahc->platform_data->hw_dma_mask);
+#else
+ ahc->dev_softc->dma_mask = ahc->platform_data->hw_dma_mask;
+#endif
+ }
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) */
+ /*
+ * At least in 2.2.14, malloc is a slab allocator so all
+ * allocations are aligned. We assume for these kernel versions
+ * that all allocations will be bellow 4Gig, physically contiguous,
+ * and accessable via DMA by the controller.
+ */
+ map = NULL; /* No additional information to store */
+ *vaddr = malloc(dmat->maxsize, M_DEVBUF, M_NOWAIT);
+#endif
+ if (*vaddr == NULL)
+ return (ENOMEM);
+ *mapp = map;
+ return(0);
+}
+
+void
+ahc_dmamem_free(struct ahc_softc *ahc, bus_dma_tag_t dmat,
+ void* vaddr, bus_dmamap_t map)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ pci_free_consistent(ahc->dev_softc, dmat->maxsize,
+ vaddr, map->bus_addr);
+#else
+ free(vaddr, M_DEVBUF);
+#endif
+}
+
+int
+ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map,
+ void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb,
+ void *cb_arg, int flags)
+{
+ /*
+ * Assume for now that this will only be used during
+ * initialization and not for per-transaction buffer mapping.
+ */
+ bus_dma_segment_t stack_sg;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ stack_sg.ds_addr = map->bus_addr;
+#else
+ stack_sg.ds_addr = VIRT_TO_BUS(buf);
+#endif
+ stack_sg.ds_len = dmat->maxsize;
+ cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0);
+ return (0);
+}
+
+void
+ahc_dmamap_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ /*
+ * The map may is NULL in our < 2.3.X implementation.
+ */
+ if (map != NULL)
+ free(map, M_DEVBUF);
+}
+
+int
+ahc_dmamap_unload(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ /* Nothing to do */
+ return (0);
+}
+
+/********************* Platform Dependent Functions ***************************/
+int
+ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc)
+{
+ int value;
+ int rvalue;
+ int lvalue;
+
+ /*
+ * Under Linux, cards are ordered as follows:
+ * 1) VLB/EISA BIOS enabled devices sorted by BIOS address.
+ * 2) PCI devices with BIOS enabled sorted by bus/slot/func.
+ * 3) All remaining VLB/EISA devices sorted by ioport.
+ * 4) All remaining PCI devices sorted by bus/slot/func.
+ */
+ value = (lahc->flags & AHC_BIOS_ENABLED)
+ - (rahc->flags & AHC_BIOS_ENABLED);
+ if (value != 0)
+ /* Controllers with BIOS enabled have a *higher* priority */
+ return (-value);
+
+ /*
+ * Same BIOS setting, now sort based on bus type.
+ * EISA and VL controllers sort together. EISA/VL
+ * have higher priority than PCI.
+ */
+ rvalue = (rahc->chip & AHC_BUS_MASK);
+ if (rvalue == AHC_VL)
+ rvalue = AHC_EISA;
+ lvalue = (lahc->chip & AHC_BUS_MASK);
+ if (lvalue == AHC_VL)
+ lvalue = AHC_EISA;
+ value = lvalue - rvalue;
+ if (value != 0)
+ return (value);
+
+ /* Still equal. Sort by BIOS address, ioport, or bus/slot/func. */
+ switch (rvalue) {
+ case AHC_PCI:
+ {
+ char primary_channel;
+
+ if (aic7xxx_reverse_scan != 0)
+ value = ahc_get_pci_bus(rahc->dev_softc)
+ - ahc_get_pci_bus(lahc->dev_softc);
+ else
+ value = ahc_get_pci_bus(lahc->dev_softc)
+ - ahc_get_pci_bus(rahc->dev_softc);
+ if (value != 0)
+ break;
+ if (aic7xxx_reverse_scan != 0)
+ value = ahc_get_pci_slot(rahc->dev_softc)
+ - ahc_get_pci_slot(lahc->dev_softc);
+ else
+ value = ahc_get_pci_slot(lahc->dev_softc)
+ - ahc_get_pci_slot(rahc->dev_softc);
+ if (value != 0)
+ break;
+ /*
+ * On multi-function devices, the user can choose
+ * to have function 1 probed before function 0.
+ * Give whichever channel is the primary channel
+ * the lowest priority.
+ */
+ primary_channel = (lahc->flags & AHC_PRIMARY_CHANNEL) + 'A';
+ value = 1;
+ if (lahc->channel == primary_channel)
+ value = -1;
+ break;
+ }
+ case AHC_EISA:
+ if ((rahc->flags & AHC_BIOS_ENABLED) != 0) {
+ value = lahc->platform_data->bios_address
+ - rahc->platform_data->bios_address;
+ } else {
+ value = lahc->bsh.ioport
+ - rahc->bsh.ioport;
+ }
+ break;
+ default:
+ panic("ahc_softc_sort: invalid bus type");
+ }
+ return (value);
+}
+
+static void
+ahc_linux_setup_tag_info(char *p, char *end)
+{
+ char *base;
+ char *tok;
+ char *tok_end;
+ char *tok_end2;
+ int i;
+ int instance;
+ int targ;
+ int done;
+ char tok_list[] = {'.', ',', '{', '}', '\0'};
+
+ if (*p != ':')
+ return;
+
+ instance = -1;
+ targ = -1;
+ done = FALSE;
+ base = p;
+ /* Forward us just past the ':' */
+ tok = base + 1;
+ tok_end = strchr(tok, '\0');
+ if (tok_end < end)
+ *tok_end = ',';
+ while (!done) {
+ switch (*tok) {
+ case '{':
+ if (instance == -1)
+ instance = 0;
+ else if (targ == -1)
+ targ = 0;
+ tok++;
+ break;
+ case '}':
+ if (targ != -1)
+ targ = -1;
+ else if (instance != -1)
+ instance = -1;
+ tok++;
+ break;
+ case ',':
+ case '.':
+ if (instance == -1)
+ done = TRUE;
+ else if (targ >= 0)
+ targ++;
+ else if (instance >= 0)
+ instance++;
+ if ((targ >= AHC_NUM_TARGETS) ||
+ (instance >= NUM_ELEMENTS(aic7xxx_tag_info)))
+ done = TRUE;
+ tok++;
+ if (!done) {
+ base = tok;
+ }
+ break;
+ case '\0':
+ done = TRUE;
+ break;
+ default:
+ done = TRUE;
+ tok_end = strchr(tok, '\0');
+ for (i = 0; tok_list[i]; i++) {
+ tok_end2 = strchr(tok, tok_list[i]);
+ if ((tok_end2) && (tok_end2 < tok_end)) {
+ tok_end = tok_end2;
+ done = FALSE;
+ }
+ }
+ if ((instance >= 0) && (targ >= 0)
+ && (instance < NUM_ELEMENTS(aic7xxx_tag_info))
+ && (targ < AHC_NUM_TARGETS)) {
+ aic7xxx_tag_info[instance].tag_commands[targ] =
+ simple_strtoul(tok, NULL, 0) & 0xff;
+ }
+ tok = tok_end;
+ break;
+ }
+ }
+ while ((p != base) && (p != NULL))
+ p = strtok(NULL, ",.");
+}
+
+/*
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic7xxx=stpwlev:1,extended
+ */
+int
+aic7xxx_setup(char *s)
+{
+ int i, n;
+ char *p;
+ char *end;
+
+ static struct {
+ const char *name;
+ uint32_t *flag;
+ } options[] = {
+ { "extended", &aic7xxx_extended },
+ { "no_reset", &aic7xxx_no_reset },
+ { "verbose", &aic7xxx_verbose },
+ { "reverse_scan", &aic7xxx_reverse_scan },
+ { "no_probe", &aic7xxx_no_probe },
+ { "periodic_otag", &aic7xxx_periodic_otag },
+ { "pci_parity", &aic7xxx_pci_parity },
+ { "seltime", &aic7xxx_seltime },
+ { "tag_info", NULL }
+ };
+
+ end = strchr(s, '\0');
+
+ for (p = strtok(s, ",."); p; p = strtok(NULL, ",.")) {
+ for (i = 0; i < NUM_ELEMENTS(options); i++) {
+ n = strlen(options[i].name);
+
+ if (strncmp(options[i].name, p, n) != 0)
+ continue;
+
+ if (strncmp(p, "tag_info", n) == 0) {
+ ahc_linux_setup_tag_info(p + n, end);
+ } else if (p[n] == ':') {
+ *(options[i].flag) =
+ simple_strtoul(p + n + 1, NULL, 0);
+ } else if (!strncmp(p, "verbose", n)) {
+ *(options[i].flag) = 1;
+ } else {
+ *(options[i].flag) = ~(*(options[i].flag));
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+__setup("aic7xxx=", aic7xxx_setup);
+#endif
+
+int aic7xxx_verbose;
+
+/*
+ * Try to detect an Adaptec 7XXX controller.
+ */
+int
+ahc_linux_detect(Scsi_Host_Template *template)
+{
+ struct ahc_softc *ahc;
+ int found;
+
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+
+ /*
+ * Sanity checking of Linux SCSI data structures so
+ * that some of our hacks^H^H^H^H^Hassumptions aren't
+ * violated.
+ */
+ if (offsetof(struct ahc_cmd_internal, end)
+ > offsetof(struct scsi_cmnd, host_scribble)) {
+ printf("ahc_linux_detect: SCSI data structures changed.\n");
+ printf("ahc_linux_detect: Unable to attach\n");
+ return (0);
+ }
+#ifdef MODULE
+ /*
+ * If we've been passed any parameters, process them now.
+ */
+ if (aic7xxx)
+ aic7xxx_setup(aic7xxx);
+ if (dummy_buffer[0] != 'P')
+ printf(KERN_WARNING
+"aic7xxx: Please read the file /usr/src/xeno/drivers/scsi/README.aic7xxx\n"
+"aic7xxx: to see the proper way to specify options to the aic7xxx module\n"
+"aic7xxx: Specifically, don't use any commas when passing arguments to\n"
+"aic7xxx: insmod or else it might trash certain memory areas.\n");
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+ template->proc_name = "aic7xxx";
+#else
+ template->proc_dir = &proc_scsi_aic7xxx;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7)
+ /*
+ * We can only map 16MB per-SG
+ * so create a sector limit of
+ * "16MB" in 2K sectors.
+ */
+ template->max_sectors = 8192;
+#endif
+
+ /*
+ * Initialize our softc list lock prior to
+ * probing for any adapters.
+ */
+ ahc_list_lockinit();
+
+#ifdef CONFIG_PCI
+ ahc_linux_pci_probe(template);
+#endif
+
+ if (aic7xxx_no_probe == 0)
+ aic7770_linux_probe(template);
+
+ /*
+ * Register with the SCSI layer all
+ * controllers we've found.
+ */
+ spin_lock_irq(&io_request_lock);
+ found = 0;
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+
+ if (ahc_linux_register_host(ahc, template) == 0)
+ found++;
+ }
+ aic7xxx_detect_complete++;
+ return (found);
+}
+
+int
+ahc_linux_register_host(struct ahc_softc *ahc, Scsi_Host_Template *template)
+{
+ char buf[80];
+ struct Scsi_Host *host;
+ char *new_name;
+ u_long s;
+
+
+ template->name = ahc->description;
+ host = scsi_register(template, sizeof(struct ahc_softc *));
+ if (host == NULL)
+ return (ENOMEM);
+
+ ahc_lock(ahc, &s);
+ *((struct ahc_softc **)host->hostdata) = ahc;
+ ahc->platform_data->host = host;
+ host->can_queue = AHC_MAX_QUEUE;
+ host->cmd_per_lun = 2;
+ host->sg_tablesize = AHC_NSEG;
+ host->select_queue_depths = ahc_linux_select_queue_depth;
+ /* XXX No way to communicate the ID for multiple channels */
+ host->this_id = ahc->our_id;
+ host->irq = ahc->platform_data->irq;
+ host->max_id = (ahc->features & AHC_WIDE) ? 16 : 8;
+ host->max_lun = AHC_NUM_LUNS;
+ host->max_channel = (ahc->features & AHC_TWIN) ? 1 : 0;
+ ahc_set_unit(ahc, ahc_linux_next_unit());
+ sprintf(buf, "scsi%d", host->host_no);
+ new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (new_name != NULL) {
+ strcpy(new_name, buf);
+ ahc_set_name(ahc, new_name);
+ }
+ host->unique_id = ahc->unit;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
+ scsi_set_pci_device(host, ahc->dev_softc);
+#endif
+ ahc_linux_initialize_scsi_bus(ahc);
+ ahc_unlock(ahc, &s);
+ return (0);
+}
+
+uint64_t
+ahc_linux_get_memsize()
+{
+ // struct sysinfo si;
+ //
+ // si_meminfo(&si);
+ // return (si.totalram << PAGE_SHIFT);
+ printf("JWS: aic7xxx: get_memsize\n");
+ return 0;
+}
+
+/*
+ * Find the smallest available unit number to use
+ * for a new device. We don't just use a static
+ * count to handle the "repeated hot-(un)plug"
+ * scenario.
+ */
+static int
+ahc_linux_next_unit()
+{
+ struct ahc_softc *ahc;
+ int unit;
+
+ unit = 0;
+retry:
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+ if (ahc->unit == unit) {
+ unit++;
+ goto retry;
+ }
+ }
+ return (unit);
+}
+
+/*
+ * Place the SCSI bus into a known state by either resetting it,
+ * or forcing transfer negotiations on the next command to any
+ * target.
+ */
+void
+ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc)
+{
+ int i;
+ int numtarg;
+
+ i = 0;
+ numtarg = 0;
+
+ if (aic7xxx_no_reset != 0)
+ ahc->flags &= ~(AHC_RESET_BUS_A|AHC_RESET_BUS_B);
+
+ if ((ahc->flags & AHC_RESET_BUS_A) != 0)
+ ahc_reset_channel(ahc, 'A', /*initiate_reset*/TRUE);
+ else
+ numtarg = (ahc->features & AHC_WIDE) ? 16 : 8;
+
+ if ((ahc->features & AHC_TWIN) != 0) {
+
+ if ((ahc->flags & AHC_RESET_BUS_B) != 0) {
+ ahc_reset_channel(ahc, 'B', /*initiate_reset*/TRUE);
+ } else {
+ if (numtarg == 0)
+ i = 8;
+ numtarg += 8;
+ }
+ }
+
+ for (; i < numtarg; i++) {
+ struct ahc_devinfo devinfo;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_id;
+ char channel;
+
+ channel = 'A';
+ our_id = ahc->our_id;
+ target_id = i;
+ if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+ channel = 'B';
+ our_id = ahc->our_id_b;
+ target_id = i % 8;
+ }
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+ target_id, &tstate);
+ tinfo->goal = tinfo->user;
+ /*
+ * Don't try negotiations that require PPR messages
+ * until we successfully retrieve Inquiry data.
+ */
+ tinfo->goal.ppr_options = 0;
+ if (tinfo->goal.transport_version > SCSI_REV_2)
+ tinfo->goal.transport_version = SCSI_REV_2;
+ ahc_compile_devinfo(&devinfo, our_id, target_id,
+ CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);
+ ahc_update_neg_request(ahc, &devinfo, tstate,
+ tinfo, /*force*/FALSE);
+ }
+ /* Give the bus some time to recover */
+ if ((ahc->flags & (AHC_RESET_BUS_A|AHC_RESET_BUS_B)) != 0) {
+ // JWS - XEN - DONT USE TIMERS HERE
+#if 0
+ ahc_linux_freeze_sim_queue(ahc);
+ init_timer(&ahc->platform_data->reset_timer);
+ ahc->platform_data->reset_timer.data = (u_long)ahc;
+ ahc->platform_data->reset_timer.expires =
+ jiffies + (AIC7XXX_RESET_DELAY * HZ)/1000;
+ ahc->platform_data->reset_timer.function =
+ ahc_linux_release_sim_queue;
+ add_timer(&ahc->platform_data->reset_timer);
+#else
+ mdelay(AIC7XXX_RESET_DELAY);
+#endif
+ }
+}
+
+int
+ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
+{
+ ahc->platform_data =
+ malloc(sizeof(struct ahc_platform_data), M_DEVBUF, M_NOWAIT);
+ if (ahc->platform_data == NULL)
+ return (ENOMEM);
+ memset(ahc->platform_data, 0, sizeof(struct ahc_platform_data));
+ TAILQ_INIT(&ahc->platform_data->completeq);
+ TAILQ_INIT(&ahc->platform_data->device_runq);
+ ahc->platform_data->irq = AHC_LINUX_NOIRQ;
+ ahc->platform_data->hw_dma_mask = 0xFFFFFFFF;
+ ahc_lockinit(ahc);
+ ahc_done_lockinit(ahc);
+#if 0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ init_MUTEX_LOCKED(&ahc->platform_data->eh_sem);
+#else
+ ahc->platform_data->eh_sem = MUTEX_LOCKED;
+#endif
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_init(&ahc->platform_data->runq_tasklet, ahc_runq_tasklet,
+ (unsigned long)ahc);
+#endif
+ ahc->seltime = (aic7xxx_seltime & 0x3) << 4;
+ ahc->seltime_b = (aic7xxx_seltime & 0x3) << 4;
+#if XENO_KILLED
+ if (TAILQ_EMPTY(&ahc_tailq))
+ register_reboot_notifier(&ahc_linux_notifier);
+#endif
+ return (0);
+}
+
+void
+ahc_platform_free(struct ahc_softc *ahc)
+{
+ if (ahc->platform_data != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_kill(&ahc->platform_data->runq_tasklet);
+#endif
+ if (ahc->platform_data->host != NULL)
+ scsi_unregister(ahc->platform_data->host);
+ if (ahc->platform_data->irq != AHC_LINUX_NOIRQ)
+ free_irq(ahc->platform_data->irq, ahc);
+ if (ahc->tag == BUS_SPACE_PIO
+ && ahc->bsh.ioport != 0)
+ release_region(ahc->bsh.ioport, 256);
+ if (ahc->tag == BUS_SPACE_MEMIO
+ && ahc->bsh.maddr != NULL) {
+ u_long base_addr;
+
+ base_addr = (u_long)ahc->bsh.maddr;
+ base_addr &= PAGE_MASK;
+ iounmap((void *)base_addr);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ release_mem_region(ahc->platform_data->mem_busaddr,
+ 0x1000);
+#endif
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /* XXX Need an instance detach in the PCI code */
+ if (ahc->dev_softc != NULL)
+ ahc->dev_softc->driver = NULL;
+#endif
+ free(ahc->platform_data, M_DEVBUF);
+ }
+ if (TAILQ_EMPTY(&ahc_tailq)) {
+#ifdef XENO_KILLED
+ unregister_reboot_notifier(&ahc_linux_notifier);
+#endif
+#ifdef CONFIG_PCI
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pci_unregister_driver(&aic7xxx_pci_driver);
+#endif
+#endif
+ }
+}
+
+void
+ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+ ahc_platform_abort_scbs(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), SCB_LIST_NULL,
+ ROLE_UNKNOWN, CAM_REQUEUE_REQ);
+}
+
+void
+ahc_platform_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ ahc_queue_alg alg)
+{
+ struct ahc_linux_device *dev;
+ int was_queuing;
+ int now_queuing;
+
+ dev = ahc_linux_get_device(ahc, devinfo->channel - 'A',
+ devinfo->target,
+ devinfo->lun, /*alloc*/FALSE);
+ if (dev == NULL)
+ return;
+ was_queuing = dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED);
+ now_queuing = alg != AHC_QUEUE_NONE;
+ if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) == 0
+ && (was_queuing != now_queuing)
+ && (dev->active != 0)) {
+ dev->flags |= AHC_DEV_FREEZE_TIL_EMPTY;
+ dev->qfrozen++;
+ }
+
+ dev->flags &= ~(AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED|AHC_DEV_PERIODIC_OTAG);
+ if (now_queuing) {
+ u_int usertags;
+
+ usertags = ahc_linux_user_tagdepth(ahc, devinfo);
+ if (!was_queuing) {
+ /*
+ * Start out agressively and allow our
+ * dynamic queue depth algorithm to take
+ * care of the rest.
+ */
+ dev->maxtags = usertags;
+ dev->openings = dev->maxtags - dev->active;
+ }
+ if (alg == AHC_QUEUE_TAGGED) {
+ dev->flags |= AHC_DEV_Q_TAGGED;
+ if (aic7xxx_periodic_otag != 0)
+ dev->flags |= AHC_DEV_PERIODIC_OTAG;
+ } else
+ dev->flags |= AHC_DEV_Q_BASIC;
+ } else {
+ /* We can only have one opening. */
+ dev->maxtags = 0;
+ dev->openings = 1 - dev->active;
+ }
+}
+
+int
+ahc_platform_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status)
+{
+ int chan;
+ int maxchan;
+ int targ;
+ int maxtarg;
+ int clun;
+ int maxlun;
+ int count;
+
+ if (tag != SCB_LIST_NULL)
+ return (0);
+
+ chan = 0;
+ if (channel != ALL_CHANNELS) {
+ chan = channel - 'A';
+ maxchan = chan + 1;
+ } else {
+ maxchan = (ahc->features & AHC_TWIN) ? 2 : 1;
+ }
+ targ = 0;
+ if (target != CAM_TARGET_WILDCARD) {
+ targ = target;
+ maxtarg = targ + 1;
+ } else {
+ maxtarg = (ahc->features & AHC_WIDE) ? 16 : 8;
+ }
+ clun = 0;
+ if (lun != CAM_LUN_WILDCARD) {
+ clun = lun;
+ maxlun = clun + 1;
+ } else {
+ maxlun = AHC_NUM_LUNS;
+ }
+
+ count = 0;
+ for (; chan < maxchan; chan++) {
+
+ for (; targ < maxtarg; targ++) {
+
+ for (; clun < maxlun; clun++) {
+ struct ahc_linux_device *dev;
+ struct ahc_busyq *busyq;
+ struct ahc_cmd *acmd;
+
+ dev = ahc_linux_get_device(ahc, chan,
+ targ, clun,
+ /*alloc*/FALSE);
+ if (dev == NULL)
+ continue;
+
+ busyq = &dev->busyq;
+ while ((acmd = TAILQ_FIRST(busyq)) != NULL) {
+ Scsi_Cmnd *cmd;
+
+ cmd = &acmd_scsi_cmd(acmd);
+ TAILQ_REMOVE(busyq, acmd,
+ acmd_links.tqe);
+ count++;
+ cmd->result = status << 16;
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+ }
+ }
+ }
+ }
+
+ return (count);
+}
+
+/*
+ * Sets the queue depth for each SCSI device hanging
+ * off the input host adapter.
+ */
+static void
+ahc_linux_select_queue_depth(struct Scsi_Host * host,
+ Scsi_Device * scsi_devs)
+{
+ Scsi_Device *device;
+ struct ahc_softc *ahc;
+ u_long flags;
+ int scbnum;
+
+ ahc = *((struct ahc_softc **)host->hostdata);
+ ahc_lock(ahc, &flags);
+ scbnum = 0;
+ for (device = scsi_devs; device != NULL; device = device->next) {
+ if (device->host == host) {
+ ahc_linux_device_queue_depth(ahc, device);
+ scbnum += device->queue_depth;
+ }
+ }
+ ahc_unlock(ahc, &flags);
+}
+
+static u_int
+ahc_linux_user_tagdepth(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ static int warned_user;
+ u_int tags;
+
+ tags = 0;
+ if ((ahc->user_discenable & devinfo->target_mask) != 0) {
+ if (warned_user == 0
+ && ahc->unit >= NUM_ELEMENTS(aic7xxx_tag_info)) {
+
+ printf("aic7xxx: WARNING, insufficient "
+ "tag_info instances for installed "
+ "controllers. Using defaults\n");
+ printf("aic7xxx: Please update the "
+ "aic7xxx_tag_info array in the "
+ "aic7xxx.c source file.\n");
+ tags = AHC_MAX_QUEUE;
+ warned_user++;
+ } else {
+ adapter_tag_info_t *tag_info;
+
+ tag_info = &aic7xxx_tag_info[ahc->unit];
+ tags = tag_info->tag_commands[devinfo->target_offset];
+ if (tags > AHC_MAX_QUEUE)
+ tags = AHC_MAX_QUEUE;
+ }
+ }
+ return (tags);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahc_linux_device_queue_depth(struct ahc_softc *ahc, Scsi_Device * device)
+{
+ struct ahc_devinfo devinfo;
+ u_int tags;
+
+ ahc_compile_devinfo(&devinfo,
+ device->channel == 0 ? ahc->our_id : ahc->our_id_b,
+ device->id, device->lun,
+ device->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+ tags = ahc_linux_user_tagdepth(ahc, &devinfo);
+ if (tags != 0
+ && device->tagged_supported != 0) {
+
+ device->queue_depth = tags;
+ ahc_set_tags(ahc, &devinfo, AHC_QUEUE_TAGGED);
+ printf("scsi%d:%c:%d:%d: Tagged Queuing enabled. Depth %d\n",
+ ahc->platform_data->host->host_no, devinfo.channel,
+ devinfo.target, devinfo.lun, tags);
+ } else {
+ /*
+ * We allow the OS to queue 2 untagged transactions to
+ * us at any time even though we can only execute them
+ * serially on the controller/device. This should remove
+ * some latency.
+ */
+ device->queue_depth = 2;
+ }
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+int
+ahc_linux_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *))
+{
+ struct ahc_softc *ahc;
+ struct ahc_linux_device *dev;
+ u_long flags;
+
+ ahc = *(struct ahc_softc **)cmd->host->hostdata;
+
+ /*
+ * Save the callback on completion function.
+ */
+ cmd->scsi_done = scsi_done;
+
+ ahc_lock(ahc, &flags);
+ dev = ahc_linux_get_device(ahc, cmd->channel, cmd->target,
+ cmd->lun, /*alloc*/TRUE);
+ if (dev == NULL) {
+ ahc_unlock(ahc, &flags);
+ printf("aic7xxx_linux_queue: Unable to allocate device!\n");
+ return (-ENOMEM);
+ }
+ cmd->result = CAM_REQ_INPROG << 16;
+ TAILQ_INSERT_TAIL(&dev->busyq, (struct ahc_cmd *)cmd, acmd_links.tqe);
+ if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) {
+ TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links);
+ dev->flags |= AHC_DEV_ON_RUN_LIST;
+ ahc_linux_run_device_queues(ahc);
+ }
+ ahc_unlock(ahc, &flags);
+ return (0);
+}
+
+static void
+ahc_linux_run_device_queue(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+ struct ahc_cmd *acmd;
+ struct scsi_cmnd *cmd;
+ struct scb *scb;
+ struct hardware_scb *hscb;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ uint16_t mask;
+
+ if ((dev->flags & AHC_DEV_ON_RUN_LIST) != 0)
+ panic("running device on run list");
+
+ while ((acmd = TAILQ_FIRST(&dev->busyq)) != NULL
+ && dev->openings > 0 && dev->qfrozen == 0) {
+
+ /*
+ * Schedule us to run later. The only reason we are not
+ * running is because the whole controller Q is frozen.
+ */
+ if (ahc->platform_data->qfrozen != 0) {
+
+ TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq,
+ dev, links);
+ dev->flags |= AHC_DEV_ON_RUN_LIST;
+ return;
+ }
+ /*
+ * Get an scb to use.
+ */
+ if ((scb = ahc_get_scb(ahc)) == NULL) {
+ TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq,
+ dev, links);
+ dev->flags |= AHC_DEV_ON_RUN_LIST;
+ ahc->flags |= AHC_RESOURCE_SHORTAGE;
+ return;
+ }
+ TAILQ_REMOVE(&dev->busyq, acmd, acmd_links.tqe);
+ cmd = &acmd_scsi_cmd(acmd);
+ scb->io_ctx = cmd;
+ scb->platform_data->dev = dev;
+ hscb = scb->hscb;
+ cmd->host_scribble = (char *)scb;
+
+ /*
+ * Fill out basics of the HSCB.
+ */
+ hscb->control = 0;
+ hscb->scsiid = BUILD_SCSIID(ahc, cmd);
+ hscb->lun = cmd->lun;
+ mask = SCB_GET_TARGET_MASK(ahc, scb);
+ tinfo = ahc_fetch_transinfo(ahc, SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_OUR_ID(scb),
+ SCB_GET_TARGET(ahc, scb), &tstate);
+ hscb->scsirate = tinfo->scsirate;
+ hscb->scsioffset = tinfo->curr.offset;
+ if ((tstate->ultraenb & mask) != 0)
+ hscb->control |= ULTRAENB;
+
+ if ((ahc->user_discenable & mask) != 0)
+ hscb->control |= DISCENB;
+
+ if ((tstate->auto_negotiate & mask) != 0) {
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ scb->hscb->control |= MK_MESSAGE;
+ }
+
+ if ((dev->flags & (AHC_DEV_Q_TAGGED|AHC_DEV_Q_BASIC)) != 0) {
+ if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH
+ && (dev->flags & AHC_DEV_Q_TAGGED) != 0) {
+ hscb->control |= MSG_ORDERED_TASK;
+ dev->commands_since_idle_or_otag = 0;
+ } else {
+ hscb->control |= MSG_SIMPLE_TASK;
+ }
+ }
+
+ hscb->cdb_len = cmd->cmd_len;
+ if (hscb->cdb_len <= 12) {
+ memcpy(hscb->shared_data.cdb, cmd->cmnd, hscb->cdb_len);
+ } else {
+ memcpy(hscb->cdb32, cmd->cmnd, hscb->cdb_len);
+ scb->flags |= SCB_CDB32_PTR;
+ }
+
+ scb->platform_data->xfer_len = 0;
+ ahc_set_residual(scb, 0);
+ ahc_set_sense_residual(scb, 0);
+ scb->sg_count = 0;
+ if (cmd->use_sg != 0) {
+ struct ahc_dma_seg *sg;
+ struct scatterlist *cur_seg;
+ struct scatterlist *end_seg;
+ int nseg;
+
+ cur_seg = (struct scatterlist *)cmd->request_buffer;
+ nseg = pci_map_sg(ahc->dev_softc, cur_seg, cmd->use_sg,
+ scsi_to_pci_dma_dir(cmd ->sc_data_direction));
+ end_seg = cur_seg + nseg;
+ /* Copy the segments into the SG list. */
+ sg = scb->sg_list;
+ /*
+ * The sg_count may be larger than nseg if
+ * a transfer crosses a 32bit page.
+ */
+ while (cur_seg < end_seg) {
+ bus_addr_t addr;
+ bus_size_t len;
+ int consumed;
+
+ addr = sg_dma_address(cur_seg);
+ len = sg_dma_len(cur_seg);
+ consumed = ahc_linux_map_seg(ahc, scb,
+ sg, addr, len);
+ sg += consumed;
+ scb->sg_count += consumed;
+ cur_seg++;
+ }
+ sg--;
+ sg->len |= ahc_htole32(AHC_DMA_LAST_SEG);
+
+ /*
+ * Reset the sg list pointer.
+ */
+ scb->hscb->sgptr =
+ ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
+
+ /*
+ * Copy the first SG into the "current"
+ * data pointer area.
+ */
+ scb->hscb->dataptr = scb->sg_list->addr;
+ scb->hscb->datacnt = scb->sg_list->len;
+ } else if (cmd->request_bufflen != 0) {
+ struct ahc_dma_seg *sg;
+ bus_addr_t addr;
+
+ sg = scb->sg_list;
+ addr = pci_map_single(ahc->dev_softc,
+ cmd->request_buffer,
+ cmd->request_bufflen,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ scb->platform_data->buf_busaddr = addr;
+ scb->sg_count = ahc_linux_map_seg(ahc, scb,
+ sg, addr,
+ cmd->request_bufflen);
+ sg->len |= ahc_htole32(AHC_DMA_LAST_SEG);
+
+ /*
+ * Reset the sg list pointer.
+ */
+ scb->hscb->sgptr =
+ ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
+
+ /*
+ * Copy the first SG into the "current"
+ * data pointer area.
+ */
+ scb->hscb->dataptr = sg->addr;
+ scb->hscb->datacnt = sg->len;
+ } else {
+ scb->hscb->sgptr = ahc_htole32(SG_LIST_NULL);
+ scb->hscb->dataptr = 0;
+ scb->hscb->datacnt = 0;
+ scb->sg_count = 0;
+ }
+
+ ahc_sync_sglist(ahc, scb, BUS_DMASYNC_PREWRITE);
+ LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links);
+ dev->openings--;
+ dev->active++;
+ dev->commands_issued++;
+ if ((dev->flags & AHC_DEV_PERIODIC_OTAG) != 0)
+ dev->commands_since_idle_or_otag++;
+
+ /*
+ * We only allow one untagged transaction
+ * per target in the initiator role unless
+ * we are storing a full busy target *lun*
+ * table in SCB space.
+ */
+ if ((scb->hscb->control & (TARGET_SCB|TAG_ENB)) == 0
+ && (ahc->features & AHC_SCB_BTT) == 0) {
+ struct scb_tailq *untagged_q;
+ int target_offset;
+
+ target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
+ untagged_q = &(ahc->untagged_queues[target_offset]);
+ TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe);
+ scb->flags |= SCB_UNTAGGEDQ;
+ if (TAILQ_FIRST(untagged_q) != scb)
+ continue;
+ }
+ scb->flags |= SCB_ACTIVE;
+ ahc_queue_scb(ahc, scb);
+ }
+}
+
+/*
+ * SCSI controller interrupt handler.
+ */
+void
+ahc_linux_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct ahc_softc *ahc;
+ struct ahc_cmd *acmd;
+ u_long flags;
+ struct ahc_linux_device *next_dev;
+
+ ahc = (struct ahc_softc *) dev_id;
+ ahc_lock(ahc, &flags);
+ ahc_intr(ahc);
+ acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+ TAILQ_INIT(&ahc->platform_data->completeq);
+ next_dev = ahc_linux_next_device_to_run(ahc);
+ ahc_unlock(ahc, &flags);
+ if (next_dev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_schedule(&ahc->platform_data->runq_tasklet);
+ do_softirq();
+#else
+ ahc_runq_tasklet((unsigned long)ahc);
+#endif
+ }
+ if (acmd != NULL)
+ ahc_linux_run_complete_queue(ahc, acmd);
+}
+
+void
+ahc_platform_flushwork(struct ahc_softc *ahc)
+{
+ struct ahc_cmd *acmd;
+
+ acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+ TAILQ_INIT(&ahc->platform_data->completeq);
+ if (acmd != NULL)
+ ahc_linux_run_complete_queue(ahc, acmd);
+}
+
+static struct ahc_linux_target*
+ahc_linux_alloc_target(struct ahc_softc *ahc, u_int channel, u_int target)
+{
+ struct ahc_linux_target *targ;
+ u_int target_offset;
+
+ targ = malloc(sizeof(*targ), M_DEVBUG, M_NOWAIT);
+ if (targ == NULL)
+ return (NULL);
+ memset(targ, 0, sizeof(*targ));
+ targ->channel = channel;
+ targ->target = target;
+ targ->ahc = ahc;
+ target_offset = target;
+ if (channel != 0)
+ target_offset += 8;
+ ahc->platform_data->targets[target_offset] = targ;
+ return (targ);
+}
+
+static void
+ahc_linux_free_target(struct ahc_softc *ahc, struct ahc_linux_target *targ)
+{
+ u_int target_offset;
+
+ target_offset = targ->target;
+ if (targ->channel != 0)
+ target_offset += 8;
+ ahc->platform_data->targets[target_offset] = NULL;
+ free(targ, M_DEVBUF);
+}
+
+static struct ahc_linux_device*
+ahc_linux_alloc_device(struct ahc_softc *ahc,
+ struct ahc_linux_target *targ, u_int lun)
+{
+ struct ahc_linux_device *dev;
+
+ dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT);
+ if (dev == NULL)
+ return (NULL);
+ memset(dev, 0, sizeof(*dev));
+ init_timer(&dev->timer);
+ TAILQ_INIT(&dev->busyq);
+ dev->flags = AHC_DEV_UNCONFIGURED;
+ dev->lun = lun;
+ dev->target = targ;
+
+ /*
+ * We start out life using untagged
+ * transactions of which we allow one.
+ */
+ dev->openings = 1;
+
+ /*
+ * Set maxtags to 0. This will be changed if we
+ * later determine that we are dealing with
+ * a tagged queuing capable device.
+ */
+ dev->maxtags = 0;
+
+ targ->refcount++;
+ targ->devices[lun] = dev;
+ return (dev);
+}
+
+static void
+ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+ struct ahc_linux_target *targ;
+
+ del_timer(&dev->timer);
+ targ = dev->target;
+ targ->devices[dev->lun] = NULL;
+ free(dev, M_DEVBUF);
+ targ->refcount--;
+ if (targ->refcount == 0)
+ ahc_linux_free_target(ahc, targ);
+}
+
+/*
+ * Return a string describing the driver.
+ */
+const char *
+ahc_linux_info(struct Scsi_Host *host)
+{
+ static char buffer[512];
+ char ahc_info[256];
+ char *bp;
+ struct ahc_softc *ahc;
+
+ bp = &buffer[0];
+ ahc = *(struct ahc_softc **)host->hostdata;
+ memset(bp, 0, sizeof(buffer));
+ strcpy(bp, "Adaptec AIC7XXX EISA/VLB/PCI SCSI HBA DRIVER, Rev ");
+ strcat(bp, AIC7XXX_DRIVER_VERSION);
+ strcat(bp, "\n");
+ strcat(bp, " <");
+ strcat(bp, ahc->description);
+ strcat(bp, ">\n");
+ strcat(bp, " ");
+ ahc_controller_info(ahc, ahc_info);
+ strcat(bp, ahc_info);
+ strcat(bp, "\n");
+
+ return (bp);
+}
+
+void
+ahc_send_async(struct ahc_softc *ahc, char channel,
+ u_int target, u_int lun, ac_code code, void *arg)
+{
+ switch (code) {
+ case AC_TRANSFER_NEG:
+ {
+ char buf[80];
+ struct ahc_linux_target *targ;
+ struct info_str info;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ int target_offset;
+
+ info.buffer = buf;
+ info.length = sizeof(buf);
+ info.offset = 0;
+ info.pos = 0;
+ tinfo = ahc_fetch_transinfo(ahc, channel,
+ channel == 'A' ? ahc->our_id
+ : ahc->our_id_b,
+ target, &tstate);
+
+ /*
+ * Don't bother reporting results while
+ * negotiations are still pending.
+ */
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options)
+ if (bootverbose == 0)
+ break;
+
+ /*
+ * Don't bother reporting results that
+ * are identical to those last reported.
+ */
+ target_offset = target;
+ if (channel == 'B')
+ target_offset += 8;
+ targ = ahc->platform_data->targets[target_offset];
+ if (targ == NULL)
+ break;
+ if (tinfo->curr.period == targ->last_tinfo.period
+ && tinfo->curr.width == targ->last_tinfo.width
+ && tinfo->curr.offset == targ->last_tinfo.offset
+ && tinfo->curr.ppr_options == targ->last_tinfo.ppr_options)
+ if (bootverbose == 0)
+ break;
+
+ targ->last_tinfo.period = tinfo->curr.period;
+ targ->last_tinfo.width = tinfo->curr.width;
+ targ->last_tinfo.offset = tinfo->curr.offset;
+ targ->last_tinfo.ppr_options = tinfo->curr.ppr_options;
+
+ printf("(%s:%c:", ahc_name(ahc), channel);
+ if (target == CAM_TARGET_WILDCARD)
+ printf("*): ");
+ else
+ printf("%d): ", target);
+ ahc_format_transinfo(&info, &tinfo->curr);
+ if (info.pos < info.length)
+ *info.buffer = '\0';
+ else
+ buf[info.length - 1] = '\0';
+ printf("%s", buf);
+ break;
+ }
+ case AC_SENT_BDR:
+ break;
+ case AC_BUS_RESET:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ if (ahc->platform_data->host != NULL) {
+ scsi_report_bus_reset(ahc->platform_data->host,
+ channel - 'A');
+ }
+#endif
+ break;
+ default:
+ panic("ahc_send_async: Unexpected async event");
+ }
+}
+
+/*
+ * Calls the higher level scsi done function and frees the scb.
+ */
+void
+ahc_done(struct ahc_softc *ahc, struct scb * scb)
+{
+ Scsi_Cmnd *cmd;
+ struct ahc_linux_device *dev;
+
+ LIST_REMOVE(scb, pending_links);
+ if ((scb->flags & SCB_UNTAGGEDQ) != 0) {
+ struct scb_tailq *untagged_q;
+ int target_offset;
+
+ target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
+ untagged_q = &(ahc->untagged_queues[target_offset]);
+ TAILQ_REMOVE(untagged_q, scb, links.tqe);
+ ahc_run_untagged_queue(ahc, untagged_q);
+ }
+
+ if ((scb->flags & SCB_ACTIVE) == 0) {
+ printf("SCB %d done'd twice\n", scb->hscb->tag);
+ ahc_dump_card_state(ahc);
+ panic("Stopping for safety");
+ }
+ cmd = scb->io_ctx;
+ dev = scb->platform_data->dev;
+ dev->active--;
+ dev->openings++;
+ ahc_linux_unmap_scb(ahc, scb);
+ if (scb->flags & SCB_SENSE) {
+ memcpy(cmd->sense_buffer, ahc_get_sense_buf(ahc, scb),
+ MIN(sizeof(struct scsi_sense_data),
+ sizeof(cmd->sense_buffer)));
+ cmd->result |= (DRIVER_SENSE << 24);
+ } else {
+ /*
+ * Guard against stale sense data.
+ * The Linux mid-layer assumes that sense
+ * was retrieved anytime the first byte of
+ * the sense buffer looks "sane".
+ */
+ cmd->sense_buffer[0] = 0;
+ }
+ if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) {
+ uint32_t amount_xferred;
+
+ amount_xferred =
+ ahc_get_transfer_length(scb) - ahc_get_residual(scb);
+ if (amount_xferred < scb->io_ctx->underflow) {
+ printf("Saw underflow (%ld of %ld bytes). "
+ "Treated as error\n",
+ ahc_get_residual(scb),
+ ahc_get_transfer_length(scb));
+ ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ } else {
+ ahc_set_transaction_status(scb, CAM_REQ_CMP);
+ ahc_linux_sniff_command(ahc, cmd, scb);
+ }
+ } else if (ahc_get_transaction_status(scb) == DID_OK) {
+ ahc_linux_handle_scsi_status(ahc, dev, scb);
+ } else if (ahc_get_transaction_status(scb) == DID_NO_CONNECT) {
+ /*
+ * Should a selection timeout kill the device?
+ * That depends on whether the selection timeout
+ * is persistent. Since we have no guarantee that
+ * the mid-layer will issue an inquiry for this device
+ * again, we can't just kill it off.
+ dev->flags |= AHC_DEV_UNCONFIGURED;
+ */
+ }
+
+ if (dev->openings == 1
+ && ahc_get_transaction_status(scb) == CAM_REQ_CMP
+ && ahc_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+ dev->tag_success_count++;
+ /*
+ * Some devices deal with temporary internal resource
+ * shortages by returning queue full. When the queue
+ * full occurrs, we throttle back. Slowly try to get
+ * back to our previous queue depth.
+ */
+ if ((dev->openings + dev->active) < dev->maxtags
+ && dev->tag_success_count > AHC_TAG_SUCCESS_INTERVAL) {
+ dev->tag_success_count = 0;
+ dev->openings++;
+ }
+
+ if (dev->active == 0)
+ dev->commands_since_idle_or_otag = 0;
+
+ if (TAILQ_EMPTY(&dev->busyq)) {
+ if ((dev->flags & AHC_DEV_UNCONFIGURED) != 0
+ && dev->active == 0)
+ ahc_linux_free_device(ahc, dev);
+ } else if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) {
+ TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links);
+ dev->flags |= AHC_DEV_ON_RUN_LIST;
+ }
+
+ if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+ printf("Recovery SCB completes\n");
+#if 0
+ up(&ahc->platform_data->eh_sem);
+#endif
+ }
+
+ ahc_free_scb(ahc, scb);
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+}
+
+static void
+ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev, struct scb *scb)
+{
+ /*
+ * We don't currently trust the mid-layer to
+ * properly deal with queue full or busy. So,
+ * when one occurs, we tell the mid-layer to
+ * unconditionally requeue the command to us
+ * so that we can retry it ourselves. We also
+ * implement our own throttling mechanism so
+ * we don't clobber the device with too many
+ * commands.
+ */
+ switch (ahc_get_scsi_status(scb)) {
+ default:
+ break;
+ case SCSI_STATUS_QUEUE_FULL:
+ {
+ /*
+ * By the time the core driver has returned this
+ * command, all other commands that were queued
+ * to us but not the device have been returned.
+ * This ensures that dev->active is equal to
+ * the number of commands actually queued to
+ * the device.
+ */
+ dev->tag_success_count = 0;
+ if (dev->active != 0) {
+ /*
+ * Drop our opening count to the number
+ * of commands currently outstanding.
+ */
+ dev->openings = 0;
+/*
+ ahc_print_path(ahc, scb);
+ printf("Dropping tag count to %d\n", dev->active);
+ */
+ if (dev->active == dev->tags_on_last_queuefull) {
+
+ dev->last_queuefull_same_count++;
+ /*
+ * If we repeatedly see a queue full
+ * at the same queue depth, this
+ * device has a fixed number of tag
+ * slots. Lock in this tag depth
+ * so we stop seeing queue fulls from
+ * this device.
+ */
+ if (dev->last_queuefull_same_count
+ == AHC_LOCK_TAGS_COUNT) {
+ dev->maxtags = dev->active;
+ ahc_print_path(ahc, scb);
+ printf("Locking max tag count at %d\n",
+ dev->active);
+ }
+ } else {
+ dev->tags_on_last_queuefull = dev->active;
+ dev->last_queuefull_same_count = 0;
+ }
+ ahc_set_transaction_status(scb, CAM_REQUEUE_REQ);
+ ahc_set_scsi_status(scb, SCSI_STATUS_OK);
+ break;
+ }
+ /*
+ * Drop down to a single opening, and treat this
+ * as if the target return BUSY SCSI status.
+ */
+ dev->openings = 1;
+ /* FALLTHROUGH */
+ }
+ case SCSI_STATUS_BUSY:
+ {
+ /*
+ * Set a short timer to defer sending commands for
+ * a bit since Linux will not delay in this case.
+ */
+ if ((dev->flags & AHC_DEV_TIMER_ACTIVE) != 0) {
+ printf("%s:%c:%d: Device Timer still active during "
+ "busy processing\n", ahc_name(ahc),
+ dev->target->channel, dev->target->target);
+ break;
+ }
+ dev->flags |= AHC_DEV_TIMER_ACTIVE;
+ dev->qfrozen++;
+ init_timer(&dev->timer);
+ dev->timer.data = (u_long)dev;
+ dev->timer.expires = jiffies + (HZ/2);
+ dev->timer.function = ahc_linux_dev_timed_unfreeze;
+ add_timer(&dev->timer);
+ break;
+ }
+ }
+}
+
+static void
+ahc_linux_filter_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd, struct scb *scb)
+{
+ switch (cmd->cmnd[0]) {
+ case INQUIRY:
+ {
+ struct ahc_devinfo devinfo;
+ struct scsi_inquiry *inq;
+ struct scsi_inquiry_data *sid;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_transinfo *user;
+ struct ahc_transinfo *goal;
+ struct ahc_transinfo *curr;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_syncrate *syncrate;
+ struct ahc_linux_device *dev;
+ u_int scsiid;
+ u_int maxsync;
+ int transferred_len;
+ int minlen;
+ u_int width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+
+ /*
+ * Validate the command. We only want to filter
+ * standard inquiry commands, not those querying
+ * Vital Product Data.
+ */
+ inq = (struct scsi_inquiry *)cmd->cmnd;
+ if ((inq->byte2 & SI_EVPD) != 0
+ || inq->page_code != 0)
+ break;
+
+ if (cmd->use_sg != 0) {
+ printf("%s: SG Inquiry response ignored\n",
+ ahc_name(ahc));
+ break;
+ }
+ transferred_len = ahc_get_transfer_length(scb)
+ - ahc_get_residual(scb);
+ sid = (struct scsi_inquiry_data *)cmd->request_buffer;
+
+ /*
+ * Determine if this lun actually exists. If so,
+ * hold on to its corresponding device structure.
+ * If not, make sure we release the device and
+ * don't bother processing the rest of this inquiry
+ * command.
+ */
+ dev = ahc_linux_get_device(ahc, cmd->channel,
+ cmd->target, cmd->lun,
+ /*alloc*/FALSE);
+ if (transferred_len >= 1
+ && SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
+
+ dev->flags &= ~AHC_DEV_UNCONFIGURED;
+ } else {
+ dev->flags |= AHC_DEV_UNCONFIGURED;
+ break;
+ }
+
+ /*
+ * Update our notion of this device's transfer
+ * negotiation capabilities.
+ */
+ scsiid = BUILD_SCSIID(ahc, cmd);
+ ahc_compile_devinfo(&devinfo, SCSIID_OUR_ID(scsiid),
+ cmd->target, cmd->lun,
+ SCSIID_CHANNEL(ahc, scsiid),
+ ROLE_INITIATOR);
+ tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ user = &tinfo->user;
+ goal = &tinfo->goal;
+ curr = &tinfo->curr;
+ width = user->width;
+ period = user->period;
+ offset = user->offset;
+ ppr_options = user->ppr_options;
+ minlen = offsetof(struct scsi_inquiry_data, version) + 1;
+ if (transferred_len >= minlen) {
+ curr->protocol_version = SID_ANSI_REV(sid);
+
+ /*
+ * Only attempt SPI3 once we've verified that
+ * the device claims to support SPI3 features.
+ */
+ if (curr->protocol_version < SCSI_REV_2)
+ curr->transport_version = SID_ANSI_REV(sid);
+ else
+ curr->transport_version = SCSI_REV_2;
+ }
+
+ minlen = offsetof(struct scsi_inquiry_data, flags) + 1;
+ if (transferred_len >= minlen
+ && (sid->additional_length + 4) >= minlen) {
+ if ((sid->flags & SID_WBus16) == 0)
+ width = MSG_EXT_WDTR_BUS_8_BIT;
+ if ((sid->flags & SID_Sync) == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ }
+ } else {
+ /* Keep current settings */
+ break;
+ }
+ minlen = offsetof(struct scsi_inquiry_data, spi3data) + 1;
+ /*
+ * This is a kludge to deal with inquiry requests that
+ * are not large enough for us to pull the spi3/4 bits.
+ * In this case, we assume that a device that tells us
+ * they can provide inquiry data that spans the SPI3
+ * bits and says its SCSI3 can handle a PPR request.
+ * If the inquiry request has sufficient buffer space to
+ * cover SPI3 bits, we honor them regardless of reported
+ * SCSI REV. We also allow any device that has had its
+ * goal ppr_options set to allow DT speeds to keep that
+ * option if a short inquiry occurs that would fail the
+ * normal tests outlined above.
+ */
+ if ((sid->additional_length + 4) >= minlen) {
+ if (transferred_len >= minlen) {
+ if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0)
+ ppr_options = 0;
+ } else if ((goal->ppr_options & MSG_EXT_PPR_DT_REQ)== 0)
+ ppr_options = 0;
+
+ if (curr->protocol_version > SCSI_REV_2)
+ curr->transport_version = 3;
+ } else {
+ ppr_options = 0;
+ }
+ ahc_validate_width(ahc, /*tinfo limit*/NULL, &width,
+ ROLE_UNKNOWN);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ maxsync = AHC_SYNCRATE_DT;
+ else if ((ahc->features & AHC_ULTRA) != 0)
+ maxsync = AHC_SYNCRATE_ULTRA;
+ else
+ maxsync = AHC_SYNCRATE_FAST;
+
+ syncrate = ahc_find_syncrate(ahc, &period,
+ &ppr_options, maxsync);
+ ahc_validate_offset(ahc, /*tinfo limit*/NULL, syncrate,
+ &offset, width, ROLE_UNKNOWN);
+ if (offset == 0 || period == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ }
+ /* Apply our filtered user settings. */
+ ahc_set_width(ahc, &devinfo, width,
+ AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_set_syncrate(ahc, &devinfo, syncrate, period,
+ offset, ppr_options, AHC_TRANS_GOAL,
+ /*paused*/FALSE);
+ break;
+ }
+ default:
+ panic("ahc_linux_filter_command: Unexpected Command type %x\n",
+ cmd->cmnd[0]);
+ break;
+ }
+}
+
+#if 0
+static void
+ahc_linux_sem_timeout(u_long arg)
+{
+ struct semaphore *sem;
+
+ sem = (struct semaphore *)arg;
+ up(sem);
+}
+#endif
+
+static void
+ahc_linux_freeze_sim_queue(struct ahc_softc *ahc)
+{
+ ahc->platform_data->qfrozen++;
+ if (ahc->platform_data->qfrozen == 1)
+ scsi_block_requests(ahc->platform_data->host);
+}
+
+static void
+ahc_linux_release_sim_queue(u_long arg)
+{
+ struct ahc_softc *ahc;
+ u_long s;
+ int unblock_reqs;
+
+ ahc = (struct ahc_softc *)arg;
+ unblock_reqs = 0;
+ ahc_lock(ahc, &s);
+ if (ahc->platform_data->qfrozen > 0)
+ ahc->platform_data->qfrozen--;
+ if (ahc->platform_data->qfrozen == 0) {
+ unblock_reqs = 1;
+ }
+ ahc_unlock(ahc, &s);
+ /*
+ * There is still a race here. The mid-layer
+ * should keep its own freeze count and use
+ * a bottom half handler to run the queues
+ * so we can unblock with our own lock held.
+ */
+ if (unblock_reqs) {
+ scsi_unblock_requests(ahc->platform_data->host);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_schedule(&ahc->platform_data->runq_tasklet);
+ do_softirq();
+#else
+ ahc_runq_tasklet((unsigned long)ahc);
+#endif
+ }
+}
+
+static void
+ahc_linux_dev_timed_unfreeze(u_long arg)
+{
+ struct ahc_linux_device *dev;
+ struct ahc_softc *ahc;
+ u_long s;
+
+ dev = (struct ahc_linux_device *)arg;
+ ahc = dev->target->ahc;
+ ahc_lock(ahc, &s);
+ dev->flags &= ~AHC_DEV_TIMER_ACTIVE;
+ if (dev->qfrozen > 0)
+ dev->qfrozen--;
+ if (dev->qfrozen == 0
+ && (dev->flags & AHC_DEV_ON_RUN_LIST) == 0)
+ ahc_linux_run_device_queue(ahc, dev);
+ ahc_unlock(ahc, &s);
+}
+
+static int
+ahc_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
+{
+ struct ahc_softc *ahc;
+ struct ahc_cmd *acmd;
+ struct ahc_cmd *list_acmd;
+ struct ahc_linux_device *dev;
+ struct scb *pending_scb;
+ u_long s;
+ u_int saved_scbptr;
+ u_int active_scb_index;
+ u_int last_phase;
+ int retval;
+ int paused;
+ int wait;
+ int disconnected;
+
+ paused = FALSE;
+ wait = FALSE;
+ ahc = *(struct ahc_softc **)cmd->host->hostdata;
+ acmd = (struct ahc_cmd *)cmd;
+
+ printf("%s:%d:%d:%d: Attempting to queue a%s message\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->lun,
+ flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");
+
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+
+ ahc_lock(ahc, &s);
+
+ /*
+ * First determine if we currently own this command.
+ * Start by searching the device queue. If not found
+ * there, check the pending_scb list. If not found
+ * at all, and the system wanted us to just abort the
+ * command return success.
+ */
+ dev = ahc_linux_get_device(ahc, cmd->channel, cmd->target,
+ cmd->lun, /*alloc*/FALSE);
+
+ if (dev == NULL) {
+ /*
+ * No target device for this command exists,
+ * so we must not still own the command.
+ */
+ printf("%s:%d:%d:%d: Is not an active device\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+ retval = SUCCESS;
+ goto no_cmd;
+ }
+
+ TAILQ_FOREACH(list_acmd, &dev->busyq, acmd_links.tqe) {
+ if (list_acmd == acmd)
+ break;
+ }
+
+ if (list_acmd != NULL) {
+ printf("%s:%d:%d:%d: Command found on device queue\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+ if (flag == SCB_ABORT) {
+ TAILQ_REMOVE(&dev->busyq, list_acmd, acmd_links.tqe);
+ cmd->result = DID_ABORT << 16;
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+ retval = SUCCESS;
+ goto done;
+ }
+ }
+
+ if ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED)) == 0
+ && ahc_search_untagged_queues(ahc, cmd, cmd->target,
+ cmd->channel + 'A', cmd->lun,
+ CAM_REQ_ABORTED, SEARCH_COMPLETE) != 0) {
+ printf("%s:%d:%d:%d: Command found on untagged queue\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+ retval = SUCCESS;
+ goto done;
+ }
+
+ /*
+ * See if we can find a matching cmd in the pending list.
+ */
+ LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+ if (pending_scb->io_ctx == cmd)
+ break;
+ }
+
+ if (pending_scb == NULL && flag == SCB_DEVICE_RESET) {
+
+ /* Any SCB for this device will do for a target reset */
+ LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+ if (ahc_match_scb(ahc, pending_scb, cmd->target,
+ cmd->channel + 'A', CAM_LUN_WILDCARD,
+ SCB_LIST_NULL, ROLE_INITIATOR) == 0)
+ break;
+ }
+ }
+
+ if (pending_scb == NULL) {
+ printf("%s:%d:%d:%d: Command not found\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+ goto no_cmd;
+ }
+
+ if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
+ /*
+ * We can't queue two recovery actions using the same SCB
+ */
+ retval = FAILED;
+ goto done;
+ }
+
+ /*
+ * Ensure that the card doesn't do anything
+ * behind our back and that no selections have occurred
+ * that have not been serviced. Also make sure that we
+ * didn't "just" miss an interrupt that would
+ * affect this cmd.
+ */
+ ahc->flags |= AHC_ALL_INTERRUPTS;
+ do {
+ if (paused)
+ ahc_unpause(ahc);
+ ahc_intr(ahc);
+ ahc_pause(ahc);
+ paused = TRUE;
+ ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+ ahc_clear_critical_section(ahc);
+ } while ((ahc_inb(ahc, INTSTAT) & INT_PEND) != 0
+ || (ahc_inb(ahc, SSTAT0) & (SELDO|SELINGO)));
+ ahc->flags &= ~AHC_ALL_INTERRUPTS;
+
+ ahc_dump_card_state(ahc);
+
+ if ((pending_scb->flags & SCB_ACTIVE) == 0) {
+ printf("%s:%d:%d:%d: Command already completed\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+ goto no_cmd;
+ }
+
+ disconnected = TRUE;
+ if (flag == SCB_ABORT) {
+ if (ahc_search_qinfifo(ahc, cmd->target, cmd->channel + 'A',
+ cmd->lun, pending_scb->hscb->tag,
+ ROLE_INITIATOR, CAM_REQ_ABORTED,
+ SEARCH_COMPLETE) > 0) {
+ printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
+ ahc_name(ahc), cmd->channel, cmd->target,
+ cmd->lun);
+ retval = SUCCESS;
+ goto done;
+ }
+ } else if (ahc_search_qinfifo(ahc, cmd->target, cmd->channel + 'A',
+ cmd->lun, pending_scb->hscb->tag,
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_COUNT) > 0) {
+ disconnected = FALSE;
+ }
+
+ if (disconnected && (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) != 0) {
+ struct scb *bus_scb;
+
+ bus_scb = ahc_lookup_scb(ahc, ahc_inb(ahc, SCB_TAG));
+ if (bus_scb == pending_scb)
+ disconnected = FALSE;
+ else if (flag != SCB_ABORT
+ && ahc_inb(ahc, SAVED_SCSIID) == pending_scb->hscb->scsiid
+ && ahc_inb(ahc, SAVED_LUN) == pending_scb->hscb->lun)
+ disconnected = FALSE;
+ }
+
+ /*
+ * At this point, pending_scb is the scb associated with the
+ * passed in command. That command is currently active on the
+ * bus, is in the disconnected state, or we're hoping to find
+ * a command for the same target active on the bus to abuse to
+ * send a BDR. Queue the appropriate message based on which of
+ * these states we are in.
+ */
+ last_phase = ahc_inb(ahc, LASTPHASE);
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ active_scb_index = ahc_inb(ahc, SCB_TAG);
+ if (last_phase != P_BUSFREE
+ && (pending_scb->hscb->tag == active_scb_index
+ || (flag == SCB_DEVICE_RESET
+ && SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)) == cmd->target))) {
+
+ /*
+ * We're active on the bus, so assert ATN
+ * and hope that the target responds.
+ */
+ pending_scb = ahc_lookup_scb(ahc, active_scb_index);
+ pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
+ printf("%s:%d:%d:%d: Device is active, asserting ATN\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+ wait = TRUE;
+ } else if (disconnected) {
+
+ /*
+ * Actually re-queue this SCB in an attempt
+ * to select the device before it reconnects.
+ * In either case (selection or reselection),
+ * we will now issue the approprate message
+ * to the timed-out device.
+ *
+ * Set the MK_MESSAGE control bit indicating
+ * that we desire to send a message. We
+ * also set the disconnected flag since
+ * in the paging case there is no guarantee
+ * that our SCB control byte matches the
+ * version on the card. We don't want the
+ * sequencer to abort the command thinking
+ * an unsolicited reselection occurred.
+ */
+ pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+ pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+
+ /*
+ * Remove any cached copy of this SCB in the
+ * disconnected list in preparation for the
+ * queuing of our abort SCB. We use the
+ * same element in the SCB, SCB_NEXT, for
+ * both the qinfifo and the disconnected list.
+ */
+ ahc_search_disc_list(ahc, cmd->target, cmd->channel + 'A',
+ cmd->lun, pending_scb->hscb->tag,
+ /*stop_on_first*/TRUE,
+ /*remove*/TRUE,
+ /*save_state*/FALSE);
+
+ /*
+ * In the non-paging case, the sequencer will
+ * never re-reference the in-core SCB.
+ * To make sure we are notified during
+ * reslection, set the MK_MESSAGE flag in
+ * the card's copy of the SCB.
+ */
+ if ((ahc->flags & AHC_PAGESCBS) == 0) {
+ ahc_outb(ahc, SCBPTR, pending_scb->hscb->tag);
+ ahc_outb(ahc, SCB_CONTROL,
+ ahc_inb(ahc, SCB_CONTROL)|MK_MESSAGE);
+ }
+
+ /*
+ * Clear out any entries in the QINFIFO first
+ * so we are the next SCB for this target
+ * to run.
+ */
+ ahc_search_qinfifo(ahc, cmd->target, cmd->channel + 'A',
+ cmd->lun, SCB_LIST_NULL, ROLE_INITIATOR,
+ CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+ ahc_print_path(ahc, pending_scb);
+ printf("Queuing a recovery SCB\n");
+ ahc_qinfifo_requeue_tail(ahc, pending_scb);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ printf("%s:%d:%d:%d: Device is disconnected, re-queuing SCB\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+ wait = TRUE;
+ } else {
+ printf("%s:%d:%d:%d: Unable to deliver message\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+ retval = FAILED;
+ goto done;
+ }
+
+no_cmd:
+ /*
+ * Our assumption is that if we don't have the command, no
+ * recovery action was required, so we return success. Again,
+ * the semantics of the mid-layer recovery engine are not
+ * well defined, so this may change in time.
+ */
+ retval = SUCCESS;
+done:
+ if (paused)
+ ahc_unpause(ahc);
+ if (wait) {
+ // JWS - XEN - err...
+ printf("JWS - aic7xxx: recovery-wait: doh\n");
+ retval=FAILED;
+ /*
+ struct timer_list timer;
+ int ret;
+
+ ahc_unlock(ahc, &s);
+ init_timer(&timer);
+ timer.data = (u_long)&ahc->platform_data->eh_sem;
+ timer.expires = jiffies + (5 * HZ);
+ timer.function = ahc_linux_sem_timeout;
+ add_timer(&timer);
+ printf("Recovery code sleeping\n");
+ down(&ahc->platform_data->eh_sem);
+ printf("Recovery code awake\n");
+ ret = del_timer(&timer);
+ if (ret == 0) {
+ printf("Timer Expired\n");
+ retval = FAILED;
+ }
+ ahc_lock(ahc, &s);
+ */
+ }
+ acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+ TAILQ_INIT(&ahc->platform_data->completeq);
+ ahc_unlock(ahc, &s);
+ if (acmd != NULL)
+ ahc_linux_run_complete_queue(ahc, acmd);
+ ahc_runq_tasklet((unsigned long)ahc);
+ spin_lock_irq(&io_request_lock);
+ return (retval);
+}
+
+/*
+ * Abort the current SCSI command(s).
+ */
+int
+ahc_linux_abort(Scsi_Cmnd *cmd)
+{
+ int error;
+
+ error = ahc_linux_queue_recovery_cmd(cmd, SCB_ABORT);
+ if (error != 0)
+ printf("aic7xxx_abort returns 0x%x\n", error);
+ return (error);
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+int
+ahc_linux_dev_reset(Scsi_Cmnd *cmd)
+{
+ int error;
+
+ error = ahc_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET);
+ if (error != 0)
+ printf("aic7xxx_dev_reset returns 0x%x\n", error);
+ return (error);
+}
+
+/*
+ * Reset the SCSI bus.
+ */
+int
+ahc_linux_bus_reset(Scsi_Cmnd *cmd)
+{
+ struct ahc_softc *ahc;
+ struct ahc_cmd *acmd;
+ u_long s;
+ int found;
+
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+
+ ahc = *(struct ahc_softc **)cmd->host->hostdata;
+ ahc_lock(ahc, &s);
+ found = ahc_reset_channel(ahc, cmd->channel + 'A',
+ /*initiate reset*/TRUE);
+ acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+ TAILQ_INIT(&ahc->platform_data->completeq);
+ ahc_unlock(ahc, &s);
+ if (bootverbose)
+ printf("%s: SCSI bus reset delivered. "
+ "%d SCBs aborted.\n", ahc_name(ahc), found);
+
+ if (acmd != NULL)
+ ahc_linux_run_complete_queue(ahc, acmd);
+
+ spin_lock_irq(&io_request_lock);
+ return SUCCESS;
+}
+
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+int
+ahc_linux_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+ int heads;
+ int sectors;
+ int cylinders;
+ //int ret;
+ int extended;
+ struct ahc_softc *ahc;
+ //struct buffer_head *bh;
+
+ ahc = *((struct ahc_softc **)disk->device->host->hostdata);
+#if 0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+ bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, block_size(dev));
+#else
+ bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024);
+#endif
+
+ if (bh) {
+ ret = scsi_partsize(bh, disk->capacity,
+ &geom[2], &geom[0], &geom[1]);
+ brelse(bh);
+ if (ret != -1)
+ return (ret);
+ }
+#endif
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ if (aic7xxx_extended != 0)
+ extended = 1;
+ else if (disk->device->channel == 0)
+ extended = (ahc->flags & AHC_EXTENDED_TRANS_A) != 0;
+ else
+ extended = (ahc->flags & AHC_EXTENDED_TRANS_B) != 0;
+ if (extended && cylinders >= 1024) {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (heads * sectors);
+ }
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ return (0);
+}
+
+/*
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ */
+int
+ahc_linux_release(struct Scsi_Host * host)
+{
+ struct ahc_softc *ahc;
+ u_long l;
+
+ ahc_list_lock(&l);
+ if (host != NULL) {
+
+ /*
+ * We should be able to just perform
+ * the free directly, but check our
+ * list for extra sanity.
+ */
+ ahc = ahc_find_softc(*(struct ahc_softc **)host->hostdata);
+ if (ahc != NULL) {
+ u_long s;
+
+ ahc_lock(ahc, &s);
+ ahc_intr_enable(ahc, FALSE);
+ ahc_unlock(ahc, &s);
+ ahc_free(ahc);
+ }
+ }
+ ahc_list_unlock(&l);
+ return (0);
+}
+
+void
+ahc_platform_dump_card_state(struct ahc_softc *ahc)
+{
+ struct ahc_linux_device *dev;
+ int channel;
+ int maxchannel;
+ int target;
+ int maxtarget;
+ int lun;
+ int i;
+
+ maxchannel = (ahc->features & AHC_TWIN) ? 1 : 0;
+ maxtarget = (ahc->features & AHC_WIDE) ? 15 : 7;
+ for (channel = 0; channel <= maxchannel; channel++) {
+
+ for (target = 0; target <=maxtarget; target++) {
+
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_cmd *acmd;
+
+ dev = ahc_linux_get_device(ahc, channel, target,
+ lun, /*alloc*/FALSE);
+ if (dev == NULL)
+ continue;
+
+ printf("DevQ(%d:%d:%d): ",
+ channel, target, lun);
+ i = 0;
+ TAILQ_FOREACH(acmd, &dev->busyq,
+ acmd_links.tqe) {
+ if (i++ > AHC_SCB_MAX)
+ break;
+ }
+ printf("%d waiting\n", i);
+ }
+ }
+ }
+}
+
+
+#if defined(MODULE) || LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static Scsi_Host_Template driver_template = AIC7XXX;
+Scsi_Host_Template *aic7xxx_driver_template = &driver_template;
+#include "../scsi_module.c.inc"
+#endif
+
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_osm.h b/xen/drivers/scsi/aic7xxx/aic7xxx_osm.h
new file mode 100644
index 0000000000..b1a84e7d62
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_osm.h
@@ -0,0 +1,1199 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * 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, 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.h#82 $
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.h#82 $
+ *
+ */
+
+#ifndef _AIC7XXX_LINUX_H_
+#define _AIC7XXX_LINUX_H_
+#include <xeno/lib.h>
+#include <xeno/string.h>
+#include <xeno/types.h>
+#include <xeno/blk.h>
+#include <xeno/blkdev.h>
+#include <xeno/delay.h>
+#include <xeno/ioport.h>
+#include <xeno/pci.h>
+//#include <xeno/version.h>
+#ifndef AHC_MODVERSION_FILE
+#define __NO_VERSION__
+#endif
+#include <xeno/module.h>
+#include <asm/byteorder.h>
+#include <xeno/notifier.h>
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
+#define LINUX_VERSION_CODE KERNEL_VERSION(2,4,20)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#include <xeno/interrupt.h> /* For tasklet support. */
+#include <xeno/config.h>
+#include <xeno/slab.h>
+#else
+#include <xeno/malloc.h>
+#endif
+
+/* Core SCSI definitions */
+#include "../scsi.h"
+#include "../hosts.h"
+
+/* Name space conflict with BSD queue macros */
+#ifdef LIST_HEAD
+#undef LIST_HEAD
+#endif
+
+#include "cam.h"
+#include "queue.h"
+#include "scsi_message.h"
+
+/************************* Forward Declarations *******************************/
+struct ahc_softc;
+typedef struct pci_dev *ahc_dev_softc_t;
+typedef Scsi_Cmnd *ahc_io_ctx_t;
+
+/******************************* Byte Order ***********************************/
+#define ahc_htobe16(x) cpu_to_be16(x)
+#define ahc_htobe32(x) cpu_to_be32(x)
+#define ahc_htobe64(x) cpu_to_be64(x)
+#define ahc_htole16(x) cpu_to_le16(x)
+#define ahc_htole32(x) cpu_to_le32(x)
+#define ahc_htole64(x) cpu_to_le64(x)
+
+#define ahc_be16toh(x) be16_to_cpu(x)
+#define ahc_be32toh(x) be32_to_cpu(x)
+#define ahc_be64toh(x) be64_to_cpu(x)
+#define ahc_le16toh(x) le16_to_cpu(x)
+#define ahc_le32toh(x) le32_to_cpu(x)
+#define ahc_le64toh(x) le64_to_cpu(x)
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#ifndef BYTE_ORDER
+#if defined(__BIG_ENDIAN)
+#define BYTE_ORDER BIG_ENDIAN
+#endif
+#if defined(__LITTLE_ENDIAN)
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif /* BYTE_ORDER */
+
+/************************* Configuration Data *********************************/
+extern int aic7xxx_no_probe;
+extern int aic7xxx_detect_complete;
+extern Scsi_Host_Template* aic7xxx_driver_template;
+
+/***************************** Bus Space/DMA **********************************/
+
+//#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,17)
+typedef dma_addr_t bus_addr_t;
+//#else
+//typedef uint32_t bus_addr_t;
+//#endif
+typedef uint32_t bus_size_t;
+
+typedef enum {
+ BUS_SPACE_MEMIO,
+ BUS_SPACE_PIO
+} bus_space_tag_t;
+
+typedef union {
+ u_long ioport;
+ volatile uint8_t *maddr;
+} bus_space_handle_t;
+
+typedef struct bus_dma_segment
+{
+ bus_addr_t ds_addr;
+ bus_size_t ds_len;
+} bus_dma_segment_t;
+
+struct ahc_linux_dma_tag
+{
+ bus_size_t alignment;
+ bus_size_t boundary;
+ bus_size_t maxsize;
+};
+typedef struct ahc_linux_dma_tag* bus_dma_tag_t;
+
+struct ahc_linux_dmamap
+{
+ bus_addr_t bus_addr;
+};
+typedef struct ahc_linux_dmamap* bus_dmamap_t;
+
+typedef int bus_dma_filter_t(void*, bus_addr_t);
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+#define BUS_DMA_WAITOK 0x0
+#define BUS_DMA_NOWAIT 0x1
+#define BUS_DMA_ALLOCNOW 0x2
+#define BUS_DMA_LOAD_SEGS 0x4 /*
+ * Argument is an S/G list not
+ * a single buffer.
+ */
+
+#define BUS_SPACE_MAXADDR 0xFFFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT 0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT 0xFFFFFFFF
+
+int ahc_dma_tag_create(struct ahc_softc *, bus_dma_tag_t /*parent*/,
+ bus_size_t /*alignment*/, bus_size_t /*boundary*/,
+ bus_addr_t /*lowaddr*/, bus_addr_t /*highaddr*/,
+ bus_dma_filter_t*/*filter*/, void */*filterarg*/,
+ bus_size_t /*maxsize*/, int /*nsegments*/,
+ bus_size_t /*maxsegsz*/, int /*flags*/,
+ bus_dma_tag_t */*dma_tagp*/);
+
+void ahc_dma_tag_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/);
+
+int ahc_dmamem_alloc(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+ void** /*vaddr*/, int /*flags*/,
+ bus_dmamap_t* /*mapp*/);
+
+void ahc_dmamem_free(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+ void* /*vaddr*/, bus_dmamap_t /*map*/);
+
+void ahc_dmamap_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/,
+ bus_dmamap_t /*map*/);
+
+int ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t /*dmat*/,
+ bus_dmamap_t /*map*/, void * /*buf*/,
+ bus_size_t /*buflen*/, bus_dmamap_callback_t *,
+ void */*callback_arg*/, int /*flags*/);
+
+int ahc_dmamap_unload(struct ahc_softc *, bus_dma_tag_t, bus_dmamap_t);
+
+/*
+ * Operations performed by ahc_dmamap_sync().
+ */
+#define BUS_DMASYNC_PREREAD 0x01 /* pre-read synchronization */
+#define BUS_DMASYNC_POSTREAD 0x02 /* post-read synchronization */
+#define BUS_DMASYNC_PREWRITE 0x04 /* pre-write synchronization */
+#define BUS_DMASYNC_POSTWRITE 0x08 /* post-write synchronization */
+
+/*
+ * XXX
+ * ahc_dmamap_sync is only used on buffers allocated with
+ * the pci_alloc_consistent() API. Although I'm not sure how
+ * this works on architectures with a write buffer, Linux does
+ * not have an API to sync "coherent" memory. Perhaps we need
+ * to do an mb()?
+ */
+#define ahc_dmamap_sync(ahc, dma_tag, dmamap, offset, len, op)
+
+/************************** SCSI Constants/Structures *************************/
+#define SCSI_REV_2 2
+#define SCSI_STATUS_OK 0x00
+#define SCSI_STATUS_CHECK_COND 0x02
+#define SCSI_STATUS_COND_MET 0x04
+#define SCSI_STATUS_BUSY 0x08
+#define SCSI_STATUS_INTERMED 0x10
+#define SCSI_STATUS_INTERMED_COND_MET 0x14
+#define SCSI_STATUS_RESERV_CONFLICT 0x18
+#define SCSI_STATUS_CMD_TERMINATED 0x22
+#define SCSI_STATUS_QUEUE_FULL 0x28
+
+/*
+ * 6 byte request sense CDB format.
+ */
+struct scsi_sense
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_sense_data
+{
+ uint8_t error_code;
+ uint8_t segment;
+ uint8_t flags;
+ uint8_t info[4];
+ uint8_t extra_len;
+ uint8_t cmd_spec_info[4];
+ uint8_t add_sense_code;
+ uint8_t add_sense_code_qual;
+ uint8_t fru;
+ uint8_t sense_key_spec[3];
+ uint8_t extra_bytes[14];
+};
+
+struct scsi_inquiry
+{
+ u_int8_t opcode;
+ u_int8_t byte2;
+#define SI_EVPD 0x01
+ u_int8_t page_code;
+ u_int8_t reserved;
+ u_int8_t length;
+ u_int8_t control;
+};
+
+struct scsi_inquiry_data
+{
+ uint8_t device;
+#define SID_TYPE(inq_data) ((inq_data)->device & 0x1f)
+#define SID_QUAL(inq_data) (((inq_data)->device & 0xE0) >> 5)
+#define SID_QUAL_LU_CONNECTED 0x00 /*
+ * The specified peripheral device
+ * type is currently connected to
+ * logical unit. If the target cannot
+ * determine whether or not a physical
+ * device is currently connected, it
+ * shall also use this peripheral
+ * qualifier when returning the INQUIRY
+ * data. This peripheral qualifier
+ * does not mean that the device is
+ * ready for access by the initiator.
+ */
+#define SID_QUAL_LU_OFFLINE 0x01 /*
+ * The target is capable of supporting
+ * the specified peripheral device type
+ * on this logical unit; however, the
+ * physical device is not currently
+ * connected to this logical unit.
+ */
+#define SID_QUAL_RSVD 0x02
+#define SID_QUAL_BAD_LU 0x03 /*
+ * The target is not capable of
+ * supporting a physical device on
+ * this logical unit. For this
+ * peripheral qualifier the peripheral
+ * device type shall be set to 1Fh to
+ * provide compatibility with previous
+ * versions of SCSI. All other
+ * peripheral device type values are
+ * reserved for this peripheral
+ * qualifier.
+ */
+#define SID_QUAL_IS_VENDOR_UNIQUE(inq_data) ((SID_QUAL(inq_data) & 0x08) != 0)
+ uint8_t dev_qual2;
+#define SID_QUAL2 0x7F
+#define SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & 0x80) != 0)
+ uint8_t version;
+#define SID_ANSI_REV(inq_data) ((inq_data)->version & 0x07)
+#define SCSI_REV_0 0
+#define SCSI_REV_CCS 1
+#define SCSI_REV_2 2
+#define SCSI_REV_SPC 3
+#define SCSI_REV_SPC2 4
+
+#define SID_ECMA 0x38
+#define SID_ISO 0xC0
+ uint8_t response_format;
+#define SID_AENC 0x80
+#define SID_TrmIOP 0x40
+ uint8_t additional_length;
+ uint8_t reserved[2];
+ uint8_t flags;
+#define SID_SftRe 0x01
+#define SID_CmdQue 0x02
+#define SID_Linked 0x08
+#define SID_Sync 0x10
+#define SID_WBus16 0x20
+#define SID_WBus32 0x40
+#define SID_RelAdr 0x80
+#define SID_VENDOR_SIZE 8
+ char vendor[SID_VENDOR_SIZE];
+#define SID_PRODUCT_SIZE 16
+ char product[SID_PRODUCT_SIZE];
+#define SID_REVISION_SIZE 4
+ char revision[SID_REVISION_SIZE];
+ /*
+ * The following fields were taken from SCSI Primary Commands - 2
+ * (SPC-2) Revision 14, Dated 11 November 1999
+ */
+#define SID_VENDOR_SPECIFIC_0_SIZE 20
+ u_int8_t vendor_specific0[SID_VENDOR_SPECIFIC_0_SIZE];
+ /*
+ * An extension of SCSI Parallel Specific Values
+ */
+#define SID_SPI_IUS 0x01
+#define SID_SPI_QAS 0x02
+#define SID_SPI_CLOCK_ST 0x00
+#define SID_SPI_CLOCK_DT 0x04
+#define SID_SPI_CLOCK_DT_ST 0x0C
+#define SID_SPI_MASK 0x0F
+ uint8_t spi3data;
+ uint8_t reserved2;
+ /*
+ * Version Descriptors, stored 2 byte values.
+ */
+ uint8_t version1[2];
+ uint8_t version2[2];
+ uint8_t version3[2];
+ uint8_t version4[2];
+ uint8_t version5[2];
+ uint8_t version6[2];
+ uint8_t version7[2];
+ uint8_t version8[2];
+
+ uint8_t reserved3[22];
+
+#define SID_VENDOR_SPECIFIC_1_SIZE 160
+ uint8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE];
+};
+
+/********************************** Includes **********************************/
+/* Host template and function declarations referenced by the template. */
+#include "aic7xxx_host.h"
+
+/* Core driver definitions */
+#include "aic7xxx.h"
+
+/* SMP support */
+//#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,17)
+#include <xeno/spinlock.h>
+//#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+//#include <xeno/smp.h>
+//#endif
+
+#define AIC7XXX_DRIVER_VERSION "6.2.8"
+
+/**************************** Front End Queues ********************************/
+/*
+ * Data structure used to cast the Linux struct scsi_cmnd to something
+ * that allows us to use the queue macros. The linux structure has
+ * plenty of space to hold the links fields as required by the queue
+ * macros, but the queue macors require them to have the correct type.
+ */
+struct ahc_cmd_internal {
+ /* Area owned by the Linux scsi layer. */
+ uint8_t private[offsetof(struct scsi_cmnd, SCp.Status)];
+ union {
+ STAILQ_ENTRY(ahc_cmd) ste;
+ LIST_ENTRY(ahc_cmd) le;
+ TAILQ_ENTRY(ahc_cmd) tqe;
+ } links;
+ uint32_t end;
+};
+
+struct ahc_cmd {
+ union {
+ struct ahc_cmd_internal icmd;
+ struct scsi_cmnd scsi_cmd;
+ } un;
+};
+
+#define acmd_icmd(cmd) ((cmd)->un.icmd)
+#define acmd_scsi_cmd(cmd) ((cmd)->un.scsi_cmd)
+#define acmd_links un.icmd.links
+
+/*************************** Device Data Structures ***************************/
+/*
+ * A per probed device structure used to deal with some error recovery
+ * scenarios that the Linux mid-layer code just doesn't know how to
+ * handle. The structure allocated for a device only becomes persistant
+ * after a successfully completed inquiry command to the target when
+ * that inquiry data indicates a lun is present.
+ */
+TAILQ_HEAD(ahc_busyq, ahc_cmd);
+typedef enum {
+ AHC_DEV_UNCONFIGURED = 0x01,
+ AHC_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */
+ AHC_DEV_TIMER_ACTIVE = 0x04, /* Our timer is active */
+ AHC_DEV_ON_RUN_LIST = 0x08, /* Queued to be run later */
+ AHC_DEV_Q_BASIC = 0x10, /* Allow basic device queuing */
+ AHC_DEV_Q_TAGGED = 0x20, /* Allow full SCSI2 command queueing */
+ AHC_DEV_PERIODIC_OTAG = 0x40 /* Send OTAG to prevent starvation */
+} ahc_dev_flags;
+
+struct ahc_linux_target;
+struct ahc_linux_device {
+ TAILQ_ENTRY(ahc_linux_device) links;
+ struct ahc_busyq busyq;
+
+ /*
+ * The number of transactions currently
+ * queued to the device.
+ */
+ int active;
+
+ /*
+ * The currently allowed number of
+ * transactions that can be queued to
+ * the device. Must be signed for
+ * conversion from tagged to untagged
+ * mode where the device may have more
+ * than one outstanding active transaction.
+ */
+ int openings;
+
+ /*
+ * A positive count indicates that this
+ * device's queue is halted.
+ */
+ u_int qfrozen;
+
+ /*
+ * Cumulative command counter.
+ */
+ u_long commands_issued;
+
+ /*
+ * The number of tagged transactions when
+ * running at our current opening level
+ * that have been successfully received by
+ * this device since the last QUEUE FULL.
+ */
+ u_int tag_success_count;
+#define AHC_TAG_SUCCESS_INTERVAL 50
+
+ ahc_dev_flags flags;
+
+ /*
+ * Per device timer.
+ */
+ struct timer_list timer;
+
+ /*
+ * The high limit for the tags variable.
+ */
+ u_int maxtags;
+
+ /*
+ * The computed number of tags outstanding
+ * at the time of the last QUEUE FULL event.
+ */
+ u_int tags_on_last_queuefull;
+
+ /*
+ * How many times we have seen a queue full
+ * with the same number of tags. This is used
+ * to stop our adaptive queue depth algorithm
+ * on devices with a fixed number of tags.
+ */
+ u_int last_queuefull_same_count;
+#define AHC_LOCK_TAGS_COUNT 50
+
+ /*
+ * How many transactions have been queued
+ * without the device going idle. We use
+ * this statistic to determine when to issue
+ * an ordered tag to prevent transaction
+ * starvation. This statistic is only updated
+ * if the AHC_DEV_PERIODIC_OTAG flag is set
+ * on this device.
+ */
+ u_int commands_since_idle_or_otag;
+#define AHC_OTAG_THRESH 500
+
+ int lun;
+ struct ahc_linux_target *target;
+};
+
+struct ahc_linux_target {
+ struct ahc_linux_device *devices[AHC_NUM_LUNS];
+ int channel;
+ int target;
+ int refcount;
+ struct ahc_transinfo last_tinfo;
+ struct ahc_softc *ahc;
+};
+
+/********************* Definitions Required by the Core ***********************/
+/*
+ * Number of SG segments we require. So long as the S/G segments for
+ * a particular transaction are allocated in a physically contiguous
+ * manner and are allocated below 4GB, the number of S/G segments is
+ * unrestricted.
+ */
+#define AHC_NSEG 128
+
+/*
+ * Per-SCB OSM storage.
+ */
+struct scb_platform_data {
+ struct ahc_linux_device *dev;
+ bus_addr_t buf_busaddr;
+ uint32_t xfer_len;
+ //#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+ uint32_t resid; /* Transfer residual */
+ //#endif
+};
+
+/*
+ * Define a structure used for each host adapter. All members are
+ * aligned on a boundary >= the size of the member to honor the
+ * alignment restrictions of the various platforms supported by
+ * this driver.
+ */
+TAILQ_HEAD(ahc_completeq, ahc_cmd);
+struct ahc_platform_data {
+ /*
+ * Fields accessed from interrupt context.
+ */
+ struct ahc_linux_target *targets[AHC_NUM_TARGETS];
+ TAILQ_HEAD(, ahc_linux_device) device_runq;
+ struct ahc_completeq completeq;
+
+ //#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ spinlock_t spin_lock;
+ //#endif
+ //#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ struct tasklet_struct runq_tasklet;
+ //#endif
+ u_int qfrozen;
+ struct timer_list reset_timer;
+ // struct semaphore eh_sem;
+ struct Scsi_Host *host; /* pointer to scsi host */
+#define AHC_LINUX_NOIRQ ((uint32_t)~0)
+ uint32_t irq; /* IRQ for this adapter */
+ uint32_t bios_address;
+ uint32_t mem_busaddr; /* Mem Base Addr */
+ bus_addr_t hw_dma_mask;
+};
+
+/************************** OS Utility Wrappers *******************************/
+#define printf printk
+#define M_NOWAIT GFP_ATOMIC
+#define M_WAITOK 0
+#define malloc(size, type, flags) kmalloc(size, flags)
+#define free(ptr, type) kfree(ptr)
+
+static __inline void ahc_delay(long);
+static __inline void
+ahc_delay(long usec)
+{
+ /*
+ * udelay on Linux can have problems for
+ * multi-millisecond waits. Wait at most
+ * 1024us per call.
+ */
+ while (usec > 0) {
+ udelay(usec % 1024);
+ usec -= 1024;
+ }
+}
+
+
+/***************************** Low Level I/O **********************************/
+#if defined(__powerpc__) || defined(__i386__) || defined(__ia64__)
+#define MMAPIO
+#endif
+
+static __inline uint8_t ahc_inb(struct ahc_softc * ahc, long port);
+static __inline void ahc_outb(struct ahc_softc * ahc, long port, uint8_t val);
+static __inline void ahc_outsb(struct ahc_softc * ahc, long port,
+ uint8_t *, int count);
+static __inline void ahc_insb(struct ahc_softc * ahc, long port,
+ uint8_t *, int count);
+
+static __inline uint8_t
+ahc_inb(struct ahc_softc * ahc, long port)
+{
+ uint8_t x;
+#ifdef MMAPIO
+
+ if (ahc->tag == BUS_SPACE_MEMIO) {
+ x = readb(ahc->bsh.maddr + port);
+ } else {
+ x = inb(ahc->bsh.ioport + port);
+ }
+#else
+ x = inb(ahc->bsh.ioport + port);
+#endif
+ mb();
+ return (x);
+}
+
+static __inline void
+ahc_outb(struct ahc_softc * ahc, long port, uint8_t val)
+{
+#ifdef MMAPIO
+ if (ahc->tag == BUS_SPACE_MEMIO) {
+ writeb(val, ahc->bsh.maddr + port);
+ } else {
+ outb(val, ahc->bsh.ioport + port);
+ }
+#else
+ outb(val, ahc->bsh.ioport + port);
+#endif
+ mb();
+}
+
+static __inline void
+ahc_outsb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+ int i;
+
+ /*
+ * There is probably a more efficient way to do this on Linux
+ * but we don't use this for anything speed critical and this
+ * should work.
+ */
+ for (i = 0; i < count; i++)
+ ahc_outb(ahc, port, *array++);
+}
+
+static __inline void
+ahc_insb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+ int i;
+
+ /*
+ * There is probably a more efficient way to do this on Linux
+ * but we don't use this for anything speed critical and this
+ * should work.
+ */
+ for (i = 0; i < count; i++)
+ *array++ = ahc_inb(ahc, port);
+}
+
+/**************************** Initialization **********************************/
+int ahc_linux_register_host(struct ahc_softc *,
+ Scsi_Host_Template *);
+
+uint64_t ahc_linux_get_memsize(void);
+
+/*************************** Pretty Printing **********************************/
+
+#define off_t unsigned long
+
+struct info_str {
+ char *buffer;
+ int length;
+ off_t offset;
+ int pos;
+};
+
+void ahc_format_transinfo(struct info_str *info,
+ struct ahc_transinfo *tinfo);
+
+
+/******************************** Locking *************************************/
+/* Lock protecting internal data structures */
+static __inline void ahc_lockinit(struct ahc_softc *);
+static __inline void ahc_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void ahc_done_lockinit(struct ahc_softc *);
+static __inline void ahc_done_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_done_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock held during ahc_list manipulation and ahc softc frees */
+extern spinlock_t ahc_list_spinlock;
+static __inline void ahc_list_lockinit(void);
+static __inline void ahc_list_lock(unsigned long *flags);
+static __inline void ahc_list_unlock(unsigned long *flags);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+static __inline void
+ahc_lockinit(struct ahc_softc *ahc)
+{
+ spin_lock_init(&ahc->platform_data->spin_lock);
+}
+
+static __inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ *flags = 0;
+ spin_lock_irqsave(&ahc->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ spin_unlock_irqrestore(&ahc->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahc_done_lockinit(struct ahc_softc *ahc)
+{
+ /* We don't own the iorequest lock, so we don't initialize it. */
+}
+
+static __inline void
+ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ *flags = 0;
+ spin_lock_irqsave(&io_request_lock, *flags);
+}
+
+static __inline void
+ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ spin_unlock_irqrestore(&io_request_lock, *flags);
+}
+
+static __inline void
+ahc_list_lockinit()
+{
+ spin_lock_init(&ahc_list_spinlock);
+}
+
+static __inline void
+ahc_list_lock(unsigned long *flags)
+{
+ *flags = 0;
+ spin_lock_irqsave(&ahc_list_spinlock, *flags);
+}
+
+static __inline void
+ahc_list_unlock(unsigned long *flags)
+{
+ spin_unlock_irqrestore(&ahc_list_spinlock, *flags);
+}
+
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+ahc_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ *flags = 0;
+ save_flags(*flags);
+ cli();
+}
+
+static __inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ restore_flags(*flags);
+}
+
+static __inline void
+ahc_done_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ /*
+ * The done lock is always held while
+ * the ahc lock is held so blocking
+ * interrupts again would have no effect.
+ */
+}
+
+static __inline void
+ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+}
+
+static __inline void
+ahc_list_lockinit()
+{
+}
+
+static __inline void
+ahc_list_lock(unsigned long *flags)
+{
+ *flags = 0;
+ save_flags(*flags);
+ cli();
+}
+
+static __inline void
+ahc_list_unlock(unsigned long *flags)
+{
+ restore_flags(*flags);
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+/******************************* PCI Definitions ******************************/
+/*
+ * PCIM_xxx: mask to locate subfield in register
+ * PCIR_xxx: config register offset
+ * PCIC_xxx: device class
+ * PCIS_xxx: device subclass
+ * PCIP_xxx: device programming interface
+ * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices)
+ * PCID_xxx: device ID
+ */
+#define PCIR_DEVVENDOR 0x00
+#define PCIR_VENDOR 0x00
+#define PCIR_DEVICE 0x02
+#define PCIR_COMMAND 0x04
+#define PCIM_CMD_PORTEN 0x0001
+#define PCIM_CMD_MEMEN 0x0002
+#define PCIM_CMD_BUSMASTEREN 0x0004
+#define PCIM_CMD_MWRICEN 0x0010
+#define PCIM_CMD_PERRESPEN 0x0040
+#define PCIR_STATUS 0x06
+#define PCIR_REVID 0x08
+#define PCIR_PROGIF 0x09
+#define PCIR_SUBCLASS 0x0a
+#define PCIR_CLASS 0x0b
+#define PCIR_CACHELNSZ 0x0c
+#define PCIR_LATTIMER 0x0d
+#define PCIR_HEADERTYPE 0x0e
+#define PCIM_MFDEV 0x80
+#define PCIR_BIST 0x0f
+#define PCIR_CAP_PTR 0x34
+
+/* config registers for header type 0 devices */
+#define PCIR_MAPS 0x10
+#define PCIR_SUBVEND_0 0x2c
+#define PCIR_SUBDEV_0 0x2e
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+extern struct pci_driver aic7xxx_pci_driver;
+#endif
+
+typedef enum
+{
+ AHC_POWER_STATE_D0,
+ AHC_POWER_STATE_D1,
+ AHC_POWER_STATE_D2,
+ AHC_POWER_STATE_D3
+} ahc_power_state;
+
+void ahc_power_state_change(struct ahc_softc *ahc,
+ ahc_power_state new_state);
+/**************************** VL/EISA Routines ********************************/
+int aic7770_linux_probe(Scsi_Host_Template *);
+int aic7770_map_registers(struct ahc_softc *ahc,
+ u_int port);
+int aic7770_map_int(struct ahc_softc *ahc, u_int irq);
+
+/******************************* PCI Routines *********************************/
+/*
+ * We need to use the bios32.h routines if we are kernel version 2.1.92 or less.
+ */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92)
+#if defined(__sparc_v9__) || defined(__powerpc__)
+#error "PPC and Sparc platforms are only support under 2.1.92 and above"
+#endif
+#include <xeno/bios32.h>
+#endif
+
+int ahc_linux_pci_probe(Scsi_Host_Template *);
+int ahc_pci_map_registers(struct ahc_softc *ahc);
+int ahc_pci_map_int(struct ahc_softc *ahc);
+
+static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t pci,
+ int reg, int width);
+
+static __inline uint32_t
+ahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width)
+{
+ switch (width) {
+ case 1:
+ {
+ uint8_t retval;
+
+ pci_read_config_byte(pci, reg, &retval);
+ return (retval);
+ }
+ case 2:
+ {
+ uint16_t retval;
+ pci_read_config_word(pci, reg, &retval);
+ return (retval);
+ }
+ case 4:
+ {
+ uint32_t retval;
+ pci_read_config_dword(pci, reg, &retval);
+ return (retval);
+ }
+ default:
+ panic("ahc_pci_read_config: Read size too big");
+ /* NOTREACHED */
+ return (0);
+ }
+}
+
+static __inline void ahc_pci_write_config(ahc_dev_softc_t pci,
+ int reg, uint32_t value,
+ int width);
+
+static __inline void
+ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+ switch (width) {
+ case 1:
+ pci_write_config_byte(pci, reg, value);
+ break;
+ case 2:
+ pci_write_config_word(pci, reg, value);
+ break;
+ case 4:
+ pci_write_config_dword(pci, reg, value);
+ break;
+ default:
+ panic("ahc_pci_write_config: Write size too big");
+ /* NOTREACHED */
+ }
+}
+
+static __inline int ahc_get_pci_function(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_function(ahc_dev_softc_t pci)
+{
+ return (PCI_FUNC(pci->devfn));
+}
+
+static __inline int ahc_get_pci_slot(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_slot(ahc_dev_softc_t pci)
+{
+ return (PCI_SLOT(pci->devfn));
+}
+
+static __inline int ahc_get_pci_bus(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_bus(ahc_dev_softc_t pci)
+{
+ return (pci->bus->number);
+}
+
+static __inline void ahc_flush_device_writes(struct ahc_softc *);
+static __inline void
+ahc_flush_device_writes(struct ahc_softc *ahc)
+{
+ /* XXX Is this sufficient for all architectures??? */
+ ahc_inb(ahc, INTSTAT);
+}
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,0)
+#define pci_map_sg(pdev, sg_list, nseg, direction) (nseg)
+#define pci_unmap_sg(pdev, sg_list, nseg, direction)
+#define sg_dma_address(sg) (VIRT_TO_BUS((sg)->address))
+#define sg_dma_len(sg) ((sg)->length)
+#define pci_map_single(pdev, buffer, bufflen, direction) \
+ (VIRT_TO_BUS(buffer))
+#define pci_unmap_single(pdev, buffer, buflen, direction)
+#endif
+
+/*********************** Transaction Access Wrappers **************************/
+static __inline void ahc_set_transaction_status(struct scb *, uint32_t);
+static __inline
+void ahc_set_transaction_status(struct scb *scb, uint32_t status)
+{
+ scb->io_ctx->result &= ~(CAM_STATUS_MASK << 16);
+ scb->io_ctx->result |= status << 16;
+}
+
+static __inline void ahc_set_scsi_status(struct scb *, uint32_t);
+static __inline
+void ahc_set_scsi_status(struct scb *scb, uint32_t status)
+{
+ scb->io_ctx->result &= ~0xFFFF;
+ scb->io_ctx->result |= status;
+}
+
+static __inline uint32_t ahc_get_transaction_status(struct scb *);
+static __inline
+uint32_t ahc_get_transaction_status(struct scb *scb)
+{
+ return ((scb->io_ctx->result >> 16) & CAM_STATUS_MASK);
+}
+
+static __inline uint32_t ahc_get_scsi_status(struct scb *);
+static __inline
+uint32_t ahc_get_scsi_status(struct scb *scb)
+{
+ return (scb->io_ctx->result & 0xFFFF);
+}
+
+static __inline void ahc_set_transaction_tag(struct scb *, int, u_int);
+static __inline
+void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+ /*
+ * Nothing to do for linux as the incoming transaction
+ * has no concept of tag/non tagged, etc.
+ */
+}
+
+static __inline u_long ahc_get_transfer_length(struct scb *);
+static __inline
+u_long ahc_get_transfer_length(struct scb *scb)
+{
+ return (scb->platform_data->xfer_len);
+}
+
+static __inline int ahc_get_transfer_dir(struct scb *);
+static __inline
+int ahc_get_transfer_dir(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,40)
+ return (scb->io_ctx->sc_data_direction);
+#else
+ if (scb->io_ctx->bufflen == 0)
+ return (CAM_DIR_NONE);
+
+ switch(scb->io_ctx->cmnd[0]) {
+ case 0x08: /* READ(6) */
+ case 0x28: /* READ(10) */
+ case 0xA8: /* READ(12) */
+ return (CAM_DIR_IN);
+ case 0x0A: /* WRITE(6) */
+ case 0x2A: /* WRITE(10) */
+ case 0xAA: /* WRITE(12) */
+ return (CAM_DIR_OUT);
+ default:
+ return (CAM_DIR_NONE);
+ }
+#endif
+}
+
+static __inline void ahc_set_residual(struct scb *, u_long);
+static __inline
+void ahc_set_residual(struct scb *scb, u_long resid)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ scb->io_ctx->resid = resid;
+#else
+ scb->platform_data->resid = resid;
+#endif
+}
+
+static __inline void ahc_set_sense_residual(struct scb *, u_long);
+static __inline
+void ahc_set_sense_residual(struct scb *scb, u_long resid)
+{
+ /* This can't be reported in Linux */
+}
+
+static __inline u_long ahc_get_residual(struct scb *);
+static __inline
+u_long ahc_get_residual(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ return (scb->io_ctx->resid);
+#else
+ return (scb->platform_data->resid);
+#endif
+}
+
+static __inline int ahc_perform_autosense(struct scb *);
+static __inline
+int ahc_perform_autosense(struct scb *scb)
+{
+ /*
+ * We always perform autosense in Linux.
+ * On other platforms this is set on a
+ * per-transaction basis.
+ */
+ return (1);
+}
+
+static __inline uint32_t
+ahc_get_sense_bufsize(struct ahc_softc *ahc, struct scb *scb)
+{
+ return (sizeof(struct scsi_sense_data));
+}
+
+static __inline void ahc_notify_xfer_settings_change(struct ahc_softc *,
+ struct ahc_devinfo *);
+static __inline void
+ahc_notify_xfer_settings_change(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo)
+{
+ /* Nothing to do here for linux */
+}
+
+static __inline void ahc_platform_scb_free(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline void
+ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb)
+{
+ ahc->flags &= ~AHC_RESOURCE_SHORTAGE;
+}
+
+int ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg);
+void ahc_platform_free(struct ahc_softc *ahc);
+void ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+static __inline void ahc_freeze_scb(struct scb *scb);
+static __inline void
+ahc_freeze_scb(struct scb *scb)
+{
+ /* Noting to do here for linux */
+}
+
+void ahc_platform_set_tags(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo, ahc_queue_alg);
+int ahc_platform_abort_scbs(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+void ahc_linux_isr(int irq, void *dev_id, struct pt_regs * regs);
+void ahc_platform_flushwork(struct ahc_softc *ahc);
+int ahc_softc_comp(struct ahc_softc *, struct ahc_softc *);
+void ahc_done(struct ahc_softc*, struct scb*);
+void ahc_send_async(struct ahc_softc *, char channel,
+ u_int target, u_int lun, ac_code, void *);
+void ahc_print_path(struct ahc_softc *, struct scb *);
+void ahc_platform_dump_card_state(struct ahc_softc *ahc);
+
+#ifdef CONFIG_PCI
+#define AHC_PCI_CONFIG 1
+#else
+#define AHC_PCI_CONFIG 0
+#endif
+#define bootverbose aic7xxx_verbose
+extern int aic7xxx_verbose;
+#endif /* _AIC7XXX_LINUX_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/xen/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
new file mode 100644
index 0000000000..95cc64c777
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
@@ -0,0 +1,416 @@
+/*
+ * Linux driver attachment glue for PCI based controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c#32 $
+ */
+
+#include "aic7xxx_osm.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+struct pci_device_id
+{
+};
+#endif
+
+static int ahc_linux_pci_dev_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+static int ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc,
+ u_long *base);
+#ifdef MMAPIO
+static int ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
+ u_long *bus_addr,
+ uint8_t **maddr);
+#endif /* MMAPIO */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static void ahc_linux_pci_dev_remove(struct pci_dev *pdev);
+
+/* We do our own ID filtering. So, grab all SCSI storage class devices. */
+static struct pci_device_id ahc_linux_pci_id_table[] = {
+ {
+ 0x9004, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
+ },
+ {
+ 0x9005, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ahc_linux_pci_id_table);
+
+struct pci_driver aic7xxx_pci_driver = {
+ name: "aic7xxx",
+ probe: ahc_linux_pci_dev_probe,
+ remove: ahc_linux_pci_dev_remove,
+ id_table: ahc_linux_pci_id_table
+};
+
+static void
+ahc_linux_pci_dev_remove(struct pci_dev *pdev)
+{
+ struct ahc_softc *ahc;
+ u_long l;
+
+ /*
+ * We should be able to just perform
+ * the free directly, but check our
+ * list for extra sanity.
+ */
+ ahc_list_lock(&l);
+ ahc = ahc_find_softc((struct ahc_softc *)pdev->driver_data);
+ if (ahc != NULL) {
+ u_long s;
+
+ ahc_lock(ahc, &s);
+ ahc_intr_enable(ahc, FALSE);
+ ahc_unlock(ahc, &s);
+ ahc_free(ahc);
+ }
+ ahc_list_unlock(&l);
+}
+#endif /* !LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */
+
+static int
+ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ char buf[80];
+ struct ahc_softc *ahc;
+ ahc_dev_softc_t pci;
+ struct ahc_pci_identity *entry;
+ char *name;
+ int error;
+
+ /*
+ * Some BIOSen report the same device multiple times.
+ */
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+ struct pci_dev *probed_pdev;
+
+ probed_pdev = ahc->dev_softc;
+ if (probed_pdev->bus->number == pdev->bus->number
+ && probed_pdev->devfn == pdev->devfn)
+ break;
+ }
+ if (ahc != NULL) {
+ /* Skip duplicate. */
+ return (-ENODEV);
+ }
+
+ pci = pdev;
+ entry = ahc_find_pci_device(pci);
+ if (entry == NULL)
+ return (-ENODEV);
+
+ /*
+ * Allocate a softc for this card and
+ * set it up for attachment by our
+ * common detect routine.
+ */
+ sprintf(buf, "ahc_pci:%d:%d:%d",
+ ahc_get_pci_bus(pci),
+ ahc_get_pci_slot(pci),
+ ahc_get_pci_function(pci));
+ name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (name == NULL)
+ return (-ENOMEM);
+ strcpy(name, buf);
+ ahc = ahc_alloc(NULL, name);
+ if (ahc == NULL)
+ return (-ENOMEM);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if (pci_enable_device(pdev)) {
+ ahc_free(ahc);
+ return (-ENODEV);
+ }
+ pci_set_master(pdev);
+
+ if (sizeof(bus_addr_t) > 4
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+ && ahc_linux_get_memsize() > 0x80000000
+ && pci_set_dma_mask(pdev, 0x7FFFFFFFFFULL) == 0) {
+#else
+ && ahc_linux_get_memsize() > 0x80000000) {
+
+ ahc->dev_softc->dma_mask =
+ (bus_addr_t)(0x7FFFFFFFFFULL & (bus_addr_t)~0);
+#endif
+ ahc->flags |= AHC_39BIT_ADDRESSING;
+ ahc->platform_data->hw_dma_mask =
+ (bus_addr_t)(0x7FFFFFFFFFULL & (bus_addr_t)~0);
+ }
+#endif
+ ahc->dev_softc = pci;
+ error = ahc_pci_config(ahc, entry);
+ if (error != 0) {
+ ahc_free(ahc);
+ return (-error);
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pci_set_drvdata(pdev, ahc);
+ if (aic7xxx_detect_complete)
+ ahc_linux_register_host(ahc, aic7xxx_driver_template);
+#endif
+ return (0);
+}
+
+int
+ahc_linux_pci_probe(Scsi_Host_Template *template)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ return (pci_module_init(&aic7xxx_pci_driver));
+#else
+ struct pci_dev *pdev;
+ u_int class;
+ int found;
+
+ /* If we don't have a PCI bus, we can't find any adapters. */
+ if (pci_present() == 0)
+ return (0);
+
+ found = 0;
+ pdev = NULL;
+ class = PCI_CLASS_STORAGE_SCSI << 8;
+ while ((pdev = pci_find_class(class, pdev)) != NULL) {
+ ahc_dev_softc_t pci;
+ int error;
+
+ pci = pdev;
+ error = ahc_linux_pci_dev_probe(pdev, /*pci_devid*/NULL);
+ if (error == 0)
+ found++;
+ }
+ return (found);
+#endif
+}
+
+static int
+ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc, u_long *base)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ *base = pci_resource_start(ahc->dev_softc, 0);
+#else
+ *base = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS, 4);
+ *base &= PCI_BASE_ADDRESS_IO_MASK;
+#endif
+ if (*base == 0)
+ return (ENOMEM);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+ if (check_region(*base, 256) != 0)
+ return (ENOMEM);
+ else
+ request_region(*base, 256, "aic7xxx");
+#else
+ if (request_region(*base, 256, "aic7xxx") == 0)
+ return (ENOMEM);
+#endif
+ return (0);
+}
+
+#ifdef MMAPIO
+static int
+ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
+ u_long *bus_addr,
+ uint8_t **maddr)
+{
+ u_long start;
+ u_long base_page;
+ u_long base_offset;
+ int error;
+
+ error = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ start = pci_resource_start(ahc->dev_softc, 1);
+ base_page = start & PAGE_MASK;
+ base_offset = start - base_page;
+#else
+ start = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS+4, 4);
+ base_offset = start & PCI_BASE_ADDRESS_MEM_MASK;
+ base_page = base_offset & PAGE_MASK;
+ base_offset -= base_page;
+#endif
+ if (start != 0) {
+ *bus_addr = start;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if (request_mem_region(start, 0x1000, "aic7xxx") == 0)
+ error = ENOMEM;
+#endif
+ if (error == 0) {
+ *maddr = ioremap_nocache(base_page, base_offset + 256);
+ if (*maddr == NULL) {
+ error = ENOMEM;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ release_mem_region(start, 0x1000);
+#endif
+ } else
+ *maddr += base_offset;
+ }
+ } else
+ error = ENOMEM;
+ return (error);
+}
+#endif /* MMAPIO */
+
+int
+ahc_pci_map_registers(struct ahc_softc *ahc)
+{
+ uint32_t command;
+ u_long base;
+ uint8_t *maddr;
+ int error;
+
+ /*
+ * If its allowed, we prefer memory mapped access.
+ */
+ command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, 4);
+ command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
+ base = 0;
+ maddr = NULL;
+#ifdef MMAPIO
+ error = ahc_linux_pci_reserve_mem_region(ahc, &base, &maddr);
+ if (error == 0) {
+ ahc->platform_data->mem_busaddr = base;
+ ahc->tag = BUS_SPACE_MEMIO;
+ ahc->bsh.maddr = maddr;
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
+ command | PCIM_CMD_MEMEN, 4);
+
+ /*
+ * Do a quick test to see if memory mapped
+ * I/O is functioning correctly.
+ */
+ if (ahc_inb(ahc, HCNTRL) == 0xFF) {
+
+ printf("aic7xxx: PCI Device %d:%d:%d "
+ "failed memory mapped test\n",
+ ahc_get_pci_bus(ahc->dev_softc),
+ ahc_get_pci_slot(ahc->dev_softc),
+ ahc_get_pci_function(ahc->dev_softc));
+ iounmap((void *)((u_long)maddr & PAGE_MASK));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ release_mem_region(ahc->platform_data->mem_busaddr,
+ 0x1000);
+#endif
+ ahc->bsh.maddr = NULL;
+ maddr = NULL;
+ } else
+ command |= PCIM_CMD_MEMEN;
+ } else {
+ printf("aic7xxx: PCI%d:%d:%d MEM region 0x%lx "
+ "unavailable. Cannot memory map device.\n",
+ ahc_get_pci_bus(ahc->dev_softc),
+ ahc_get_pci_slot(ahc->dev_softc),
+ ahc_get_pci_function(ahc->dev_softc),
+ base);
+ }
+#endif /* MMAPIO */
+
+ /*
+ * We always prefer memory mapped access.
+ */
+ if (maddr == NULL) {
+
+ error = ahc_linux_pci_reserve_io_region(ahc, &base);
+ if (error == 0) {
+ ahc->tag = BUS_SPACE_PIO;
+ ahc->bsh.ioport = base;
+ command |= PCIM_CMD_PORTEN;
+ } else {
+ printf("aic7xxx: PCI%d:%d:%d IO region 0x%lx[0..255] "
+ "unavailable. Cannot map device.\n",
+ ahc_get_pci_bus(ahc->dev_softc),
+ ahc_get_pci_slot(ahc->dev_softc),
+ ahc_get_pci_function(ahc->dev_softc),
+ base);
+ }
+ }
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
+ return (error);
+}
+
+int
+ahc_pci_map_int(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = request_irq(ahc->dev_softc->irq, ahc_linux_isr,
+ SA_SHIRQ, "aic7xxx", ahc);
+ if (error == 0)
+ ahc->platform_data->irq = ahc->dev_softc->irq;
+
+ return (-error);
+}
+
+void
+ahc_power_state_change(struct ahc_softc *ahc, ahc_power_state new_state)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pci_set_power_state(ahc->dev_softc, new_state);
+#else
+ uint32_t cap;
+ u_int cap_offset;
+
+ /*
+ * Traverse the capability list looking for
+ * the power management capability.
+ */
+ cap = 0;
+ cap_offset = ahc_pci_read_config(ahc->dev_softc,
+ PCIR_CAP_PTR, /*bytes*/1);
+ while (cap_offset != 0) {
+
+ cap = ahc_pci_read_config(ahc->dev_softc,
+ cap_offset, /*bytes*/4);
+ if ((cap & 0xFF) == 1
+ && ((cap >> 16) & 0x3) > 0) {
+ uint32_t pm_control;
+
+ pm_control = ahc_pci_read_config(ahc->dev_softc,
+ cap_offset + 4,
+ /*bytes*/4);
+ pm_control &= ~0x3;
+ pm_control |= new_state;
+ ahc_pci_write_config(ahc->dev_softc,
+ cap_offset + 4,
+ pm_control, /*bytes*/2);
+ break;
+ }
+ cap_offset = (cap >> 8) & 0xFF;
+ }
+#endif
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_pci.c b/xen/drivers/scsi/aic7xxx/aic7xxx_pci.c
new file mode 100644
index 0000000000..6108b4f709
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_pci.c
@@ -0,0 +1,2277 @@
+/*
+ * Product specific probe and attach routines for:
+ * 3940, 2940, aic7895, aic7890, aic7880,
+ * aic7870, aic7860 and aic7850 SCSI controllers
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_pci.c#46 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_pci.c,v 1.2.2.14 2002/04/29 19:36:31 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+#define AHC_PCI_IOADDR PCIR_MAPS /* I/O Address */
+#define AHC_PCI_MEMADDR (PCIR_MAPS + 4) /* Mem I/O Address */
+
+static __inline uint64_t
+ahc_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+ uint64_t id;
+
+ id = subvendor
+ | (subdevice << 16)
+ | ((uint64_t)vendor << 32)
+ | ((uint64_t)device << 48);
+
+ return (id);
+}
+
+#define ID_ALL_MASK 0xFFFFFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK 0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK 0xFFF0FFFF00000000ull
+#define ID_9005_SISL_MASK 0x000FFFFF00000000ull
+#define ID_9005_SISL_ID 0x0005900500000000ull
+#define ID_AIC7850 0x5078900400000000ull
+#define ID_AHA_2902_04_10_15_20_30C 0x5078900478509004ull
+#define ID_AIC7855 0x5578900400000000ull
+#define ID_AIC7859 0x3860900400000000ull
+#define ID_AHA_2930CU 0x3860900438699004ull
+#define ID_AIC7860 0x6078900400000000ull
+#define ID_AIC7860C 0x6078900478609004ull
+#define ID_AHA_1480A 0x6075900400000000ull
+#define ID_AHA_2940AU_0 0x6178900400000000ull
+#define ID_AHA_2940AU_1 0x6178900478619004ull
+#define ID_AHA_2940AU_CN 0x2178900478219004ull
+#define ID_AHA_2930C_VAR 0x6038900438689004ull
+
+#define ID_AIC7870 0x7078900400000000ull
+#define ID_AHA_2940 0x7178900400000000ull
+#define ID_AHA_3940 0x7278900400000000ull
+#define ID_AHA_398X 0x7378900400000000ull
+#define ID_AHA_2944 0x7478900400000000ull
+#define ID_AHA_3944 0x7578900400000000ull
+#define ID_AHA_4944 0x7678900400000000ull
+
+#define ID_AIC7880 0x8078900400000000ull
+#define ID_AIC7880_B 0x8078900478809004ull
+#define ID_AHA_2940U 0x8178900400000000ull
+#define ID_AHA_3940U 0x8278900400000000ull
+#define ID_AHA_2944U 0x8478900400000000ull
+#define ID_AHA_3944U 0x8578900400000000ull
+#define ID_AHA_398XU 0x8378900400000000ull
+#define ID_AHA_4944U 0x8678900400000000ull
+#define ID_AHA_2940UB 0x8178900478819004ull
+#define ID_AHA_2930U 0x8878900478889004ull
+#define ID_AHA_2940U_PRO 0x8778900478879004ull
+#define ID_AHA_2940U_CN 0x0078900478009004ull
+
+#define ID_AIC7895 0x7895900478959004ull
+#define ID_AIC7895_ARO 0x7890900478939004ull
+#define ID_AIC7895_ARO_MASK 0xFFF0FFFFFFFFFFFFull
+#define ID_AHA_2940U_DUAL 0x7895900478919004ull
+#define ID_AHA_3940AU 0x7895900478929004ull
+#define ID_AHA_3944AU 0x7895900478949004ull
+
+#define ID_AIC7890 0x001F9005000F9005ull
+#define ID_AIC7890_ARO 0x00139005000F9005ull
+#define ID_AAA_131U2 0x0013900500039005ull
+#define ID_AHA_2930U2 0x0011900501819005ull
+#define ID_AHA_2940U2B 0x00109005A1009005ull
+#define ID_AHA_2940U2_OEM 0x0010900521809005ull
+#define ID_AHA_2940U2 0x00109005A1809005ull
+#define ID_AHA_2950U2B 0x00109005E1009005ull
+
+#define ID_AIC7892 0x008F9005FFFF9005ull
+#define ID_AIC7892_ARO 0x00839005FFFF9005ull
+#define ID_AHA_29160 0x00809005E2A09005ull
+#define ID_AHA_29160_CPQ 0x00809005E2A00E11ull
+#define ID_AHA_29160N 0x0080900562A09005ull
+#define ID_AHA_29160C 0x0080900562209005ull
+#define ID_AHA_29160B 0x00809005E2209005ull
+#define ID_AHA_19160B 0x0081900562A19005ull
+
+#define ID_AIC7896 0x005F9005FFFF9005ull
+#define ID_AIC7896_ARO 0x00539005FFFF9005ull
+#define ID_AHA_3950U2B_0 0x00509005FFFF9005ull
+#define ID_AHA_3950U2B_1 0x00509005F5009005ull
+#define ID_AHA_3950U2D_0 0x00519005FFFF9005ull
+#define ID_AHA_3950U2D_1 0x00519005B5009005ull
+
+#define ID_AIC7899 0x00CF9005FFFF9005ull
+#define ID_AIC7899_ARO 0x00C39005FFFF9005ull
+#define ID_AHA_3960D 0x00C09005F6209005ull
+#define ID_AHA_3960D_CPQ 0x00C09005F6200E11ull
+
+#define ID_AIC7810 0x1078900400000000ull
+#define ID_AIC7815 0x7815900400000000ull
+
+#define DEVID_9005_TYPE(id) ((id) & 0xF)
+#define DEVID_9005_TYPE_HBA 0x0 /* Standard Card */
+#define DEVID_9005_TYPE_AAA 0x3 /* RAID Card */
+#define DEVID_9005_TYPE_SISL 0x5 /* Container ROMB */
+#define DEVID_9005_TYPE_MB 0xF /* On Motherboard */
+
+#define DEVID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define DEVID_9005_MAXRATE_U160 0x0
+#define DEVID_9005_MAXRATE_ULTRA2 0x1
+#define DEVID_9005_MAXRATE_ULTRA 0x2
+#define DEVID_9005_MAXRATE_FAST 0x3
+
+#define DEVID_9005_MFUNC(id) (((id) & 0x40) >> 6)
+
+#define DEVID_9005_CLASS(id) (((id) & 0xFF00) >> 8)
+#define DEVID_9005_CLASS_SPI 0x0 /* Parallel SCSI */
+
+#define SUBID_9005_TYPE(id) ((id) & 0xF)
+#define SUBID_9005_TYPE_MB 0xF /* On Motherboard */
+#define SUBID_9005_TYPE_CARD 0x0 /* Standard Card */
+#define SUBID_9005_TYPE_LCCARD 0x1 /* Low Cost Card */
+#define SUBID_9005_TYPE_RAID 0x3 /* Combined with Raid */
+
+#define SUBID_9005_TYPE_KNOWN(id) \
+ ((((id) & 0xF) == SUBID_9005_TYPE_MB) \
+ || (((id) & 0xF) == SUBID_9005_TYPE_CARD) \
+ || (((id) & 0xF) == SUBID_9005_TYPE_LCCARD) \
+ || (((id) & 0xF) == SUBID_9005_TYPE_RAID))
+
+#define SUBID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define SUBID_9005_MAXRATE_ULTRA2 0x0
+#define SUBID_9005_MAXRATE_ULTRA 0x1
+#define SUBID_9005_MAXRATE_U160 0x2
+#define SUBID_9005_MAXRATE_RESERVED 0x3
+
+#define SUBID_9005_SEEPTYPE(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? ((id) & 0xC0) >> 6 \
+ : ((id) & 0x300) >> 8)
+#define SUBID_9005_SEEPTYPE_NONE 0x0
+#define SUBID_9005_SEEPTYPE_1K 0x1
+#define SUBID_9005_SEEPTYPE_2K_4K 0x2
+#define SUBID_9005_SEEPTYPE_RESERVED 0x3
+#define SUBID_9005_AUTOTERM(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? (((id) & 0x400) >> 10) == 0 \
+ : (((id) & 0x40) >> 6) == 0)
+
+#define SUBID_9005_NUMCHAN(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? ((id) & 0x300) >> 8 \
+ : ((id) & 0xC00) >> 10)
+
+#define SUBID_9005_LEGACYCONN(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? 0 \
+ : ((id) & 0x80) >> 7)
+
+#define SUBID_9005_MFUNCENB(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? ((id) & 0x800) >> 11 \
+ : ((id) & 0x1000) >> 12)
+/*
+ * Informational only. Should use chip register to be
+ * certain, but may be use in identification strings.
+ */
+#define SUBID_9005_CARD_SCSIWIDTH_MASK 0x2000
+#define SUBID_9005_CARD_PCIWIDTH_MASK 0x4000
+#define SUBID_9005_CARD_SEDIFF_MASK 0x8000
+
+static ahc_device_setup_t ahc_aic785X_setup;
+static ahc_device_setup_t ahc_aic7860_setup;
+static ahc_device_setup_t ahc_apa1480_setup;
+static ahc_device_setup_t ahc_aic7870_setup;
+static ahc_device_setup_t ahc_aha394X_setup;
+static ahc_device_setup_t ahc_aha494X_setup;
+static ahc_device_setup_t ahc_aha398X_setup;
+static ahc_device_setup_t ahc_aic7880_setup;
+static ahc_device_setup_t ahc_aha2940Pro_setup;
+static ahc_device_setup_t ahc_aha394XU_setup;
+static ahc_device_setup_t ahc_aha398XU_setup;
+static ahc_device_setup_t ahc_aic7890_setup;
+static ahc_device_setup_t ahc_aic7892_setup;
+static ahc_device_setup_t ahc_aic7895_setup;
+static ahc_device_setup_t ahc_aic7896_setup;
+static ahc_device_setup_t ahc_aic7899_setup;
+static ahc_device_setup_t ahc_aha29160C_setup;
+static ahc_device_setup_t ahc_raid_setup;
+static ahc_device_setup_t ahc_aha394XX_setup;
+static ahc_device_setup_t ahc_aha494XX_setup;
+static ahc_device_setup_t ahc_aha398XX_setup;
+
+struct ahc_pci_identity ahc_pci_ident_table [] =
+{
+ /* aic7850 based controllers */
+ {
+ ID_AHA_2902_04_10_15_20_30C,
+ ID_ALL_MASK,
+ "Adaptec 2902/04/10/15/20/30C SCSI adapter",
+ ahc_aic785X_setup
+ },
+ /* aic7860 based controllers */
+ {
+ ID_AHA_2930CU,
+ ID_ALL_MASK,
+ "Adaptec 2930CU SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_1480A & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 1480A Ultra SCSI adapter",
+ ahc_apa1480_setup
+ },
+ {
+ ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940A Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940A/CN Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2930C Ultra SCSI adapter (VAR)",
+ ahc_aic7860_setup
+ },
+ /* aic7870 based controllers */
+ {
+ ID_AHA_2940,
+ ID_ALL_MASK,
+ "Adaptec 2940 SCSI adapter",
+ ahc_aic7870_setup
+ },
+ {
+ ID_AHA_3940,
+ ID_ALL_MASK,
+ "Adaptec 3940 SCSI adapter",
+ ahc_aha394X_setup
+ },
+ {
+ ID_AHA_398X,
+ ID_ALL_MASK,
+ "Adaptec 398X SCSI RAID adapter",
+ ahc_aha398X_setup
+ },
+ {
+ ID_AHA_2944,
+ ID_ALL_MASK,
+ "Adaptec 2944 SCSI adapter",
+ ahc_aic7870_setup
+ },
+ {
+ ID_AHA_3944,
+ ID_ALL_MASK,
+ "Adaptec 3944 SCSI adapter",
+ ahc_aha394X_setup
+ },
+ {
+ ID_AHA_4944,
+ ID_ALL_MASK,
+ "Adaptec 4944 SCSI adapter",
+ ahc_aha494X_setup
+ },
+ /* aic7880 based controllers */
+ {
+ ID_AHA_2940U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_3940U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 3940 Ultra SCSI adapter",
+ ahc_aha394XU_setup
+ },
+ {
+ ID_AHA_2944U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2944 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_3944U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 3944 Ultra SCSI adapter",
+ ahc_aha394XU_setup
+ },
+ {
+ ID_AHA_398XU & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 398X Ultra SCSI RAID adapter",
+ ahc_aha398XU_setup
+ },
+ {
+ /*
+ * XXX Don't know the slot numbers
+ * so we can't identify channels
+ */
+ ID_AHA_4944U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 4944 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_2930U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2930 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940 Pro Ultra SCSI adapter",
+ ahc_aha2940Pro_setup
+ },
+ {
+ ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940/CN Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ /* Ignore all SISL (AAC on MB) based controllers. */
+ {
+ ID_9005_SISL_ID,
+ ID_9005_SISL_MASK,
+ NULL,
+ NULL
+ },
+ /* aic7890 based controllers */
+ {
+ ID_AHA_2930U2,
+ ID_ALL_MASK,
+ "Adaptec 2930 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2940U2B,
+ ID_ALL_MASK,
+ "Adaptec 2940B Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2940U2_OEM,
+ ID_ALL_MASK,
+ "Adaptec 2940 Ultra2 SCSI adapter (OEM)",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2940U2,
+ ID_ALL_MASK,
+ "Adaptec 2940 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2950U2B,
+ ID_ALL_MASK,
+ "Adaptec 2950 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AIC7890_ARO,
+ ID_ALL_MASK,
+ "Adaptec aic7890/91 Ultra2 SCSI adapter (ARO)",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AAA_131U2,
+ ID_ALL_MASK,
+ "Adaptec AAA-131 Ultra2 RAID adapter",
+ ahc_aic7890_setup
+ },
+ /* aic7892 based controllers */
+ {
+ ID_AHA_29160,
+ ID_ALL_MASK,
+ "Adaptec 29160 Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_29160_CPQ,
+ ID_ALL_MASK,
+ "Adaptec (Compaq OEM) 29160 Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_29160N,
+ ID_ALL_MASK,
+ "Adaptec 29160N Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_29160C,
+ ID_ALL_MASK,
+ "Adaptec 29160C Ultra160 SCSI adapter",
+ ahc_aha29160C_setup
+ },
+ {
+ ID_AHA_29160B,
+ ID_ALL_MASK,
+ "Adaptec 29160B Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_19160B,
+ ID_ALL_MASK,
+ "Adaptec 19160B Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AIC7892_ARO,
+ ID_ALL_MASK,
+ "Adaptec aic7892 Ultra160 SCSI adapter (ARO)",
+ ahc_aic7892_setup
+ },
+ /* aic7895 based controllers */
+ {
+ ID_AHA_2940U_DUAL,
+ ID_ALL_MASK,
+ "Adaptec 2940/DUAL Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AHA_3940AU,
+ ID_ALL_MASK,
+ "Adaptec 3940A Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AHA_3944AU,
+ ID_ALL_MASK,
+ "Adaptec 3944A Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AIC7895_ARO,
+ ID_AIC7895_ARO_MASK,
+ "Adaptec aic7895 Ultra SCSI adapter (ARO)",
+ ahc_aic7895_setup
+ },
+ /* aic7896/97 based controllers */
+ {
+ ID_AHA_3950U2B_0,
+ ID_ALL_MASK,
+ "Adaptec 3950B Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AHA_3950U2B_1,
+ ID_ALL_MASK,
+ "Adaptec 3950B Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AHA_3950U2D_0,
+ ID_ALL_MASK,
+ "Adaptec 3950D Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AHA_3950U2D_1,
+ ID_ALL_MASK,
+ "Adaptec 3950D Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AIC7896_ARO,
+ ID_ALL_MASK,
+ "Adaptec aic7896/97 Ultra2 SCSI adapter (ARO)",
+ ahc_aic7896_setup
+ },
+ /* aic7899 based controllers */
+ {
+ ID_AHA_3960D,
+ ID_ALL_MASK,
+ "Adaptec 3960D Ultra160 SCSI adapter",
+ ahc_aic7899_setup
+ },
+ {
+ ID_AHA_3960D_CPQ,
+ ID_ALL_MASK,
+ "Adaptec (Compaq OEM) 3960D Ultra160 SCSI adapter",
+ ahc_aic7899_setup
+ },
+ {
+ ID_AIC7899_ARO,
+ ID_ALL_MASK,
+ "Adaptec aic7899 Ultra160 SCSI adapter (ARO)",
+ ahc_aic7899_setup
+ },
+ /* Generic chip probes for devices we don't know 'exactly' */
+ {
+ ID_AIC7850 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7850 SCSI adapter",
+ ahc_aic785X_setup
+ },
+ {
+ ID_AIC7855 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7855 SCSI adapter",
+ ahc_aic785X_setup
+ },
+ {
+ ID_AIC7859 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7859 SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AIC7860 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7860 Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AIC7870 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7870 SCSI adapter",
+ ahc_aic7870_setup
+ },
+ {
+ ID_AIC7880 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7880 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AIC7890 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7890/91 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AIC7892 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7892 Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AIC7895 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7895 Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AIC7896 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7896/97 Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AIC7899 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7899 Ultra160 SCSI adapter",
+ ahc_aic7899_setup
+ },
+ {
+ ID_AIC7810 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7810 RAID memory controller",
+ ahc_raid_setup
+ },
+ {
+ ID_AIC7815 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7815 RAID memory controller",
+ ahc_raid_setup
+ }
+};
+
+const u_int ahc_num_pci_devs = NUM_ELEMENTS(ahc_pci_ident_table);
+
+#define AHC_394X_SLOT_CHANNEL_A 4
+#define AHC_394X_SLOT_CHANNEL_B 5
+
+#define AHC_398X_SLOT_CHANNEL_A 4
+#define AHC_398X_SLOT_CHANNEL_B 8
+#define AHC_398X_SLOT_CHANNEL_C 12
+
+#define AHC_494X_SLOT_CHANNEL_A 4
+#define AHC_494X_SLOT_CHANNEL_B 5
+#define AHC_494X_SLOT_CHANNEL_C 6
+#define AHC_494X_SLOT_CHANNEL_D 7
+
+#define DEVCONFIG 0x40
+#define SCBSIZE32 0x00010000ul /* aic789X only */
+#define REXTVALID 0x00001000ul /* ultra cards only */
+#define MPORTMODE 0x00000400ul /* aic7870+ only */
+#define RAMPSM 0x00000200ul /* aic7870+ only */
+#define VOLSENSE 0x00000100ul
+#define PCI64BIT 0x00000080ul /* 64Bit PCI bus (Ultra2 Only)*/
+#define SCBRAMSEL 0x00000080ul
+#define MRDCEN 0x00000040ul
+#define EXTSCBTIME 0x00000020ul /* aic7870 only */
+#define EXTSCBPEN 0x00000010ul /* aic7870 only */
+#define BERREN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
+
+#define CSIZE_LATTIME 0x0c
+#define CACHESIZE 0x0000003ful /* only 5 bits */
+#define LATTIME 0x0000ff00ul
+
+static int ahc_9005_subdevinfo_valid(uint16_t vendor, uint16_t device,
+ uint16_t subvendor, uint16_t subdevice);
+static int ahc_ext_scbram_present(struct ahc_softc *ahc);
+static void ahc_scbram_config(struct ahc_softc *ahc, int enable,
+ int pcheck, int fast, int large);
+static void ahc_probe_ext_scbram(struct ahc_softc *ahc);
+static void check_extport(struct ahc_softc *ahc, u_int *sxfrctl1);
+static void ahc_parse_pci_eeprom(struct ahc_softc *ahc,
+ struct seeprom_config *sc);
+static void configure_termination(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd,
+ u_int adapter_control,
+ u_int *sxfrctl1);
+
+static void ahc_new_term_detect(struct ahc_softc *ahc,
+ int *enableSEC_low,
+ int *enableSEC_high,
+ int *enablePRI_low,
+ int *enablePRI_high,
+ int *eeprom_present);
+static void aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *internal68_present,
+ int *externalcable_present,
+ int *eeprom_present);
+static void aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *externalcable_present,
+ int *eeprom_present);
+static void write_brdctl(struct ahc_softc *ahc, uint8_t value);
+static uint8_t read_brdctl(struct ahc_softc *ahc);
+
+static int
+ahc_9005_subdevinfo_valid(uint16_t device, uint16_t vendor,
+ uint16_t subdevice, uint16_t subvendor)
+{
+ int result;
+
+ /* Default to invalid. */
+ result = 0;
+ if (vendor == 0x9005
+ && subvendor == 0x9005
+ && subdevice != device
+ && SUBID_9005_TYPE_KNOWN(subdevice) != 0) {
+
+ switch (SUBID_9005_TYPE(subdevice)) {
+ case SUBID_9005_TYPE_MB:
+ break;
+ case SUBID_9005_TYPE_CARD:
+ case SUBID_9005_TYPE_LCCARD:
+ /*
+ * Currently only trust Adaptec cards to
+ * get the sub device info correct.
+ */
+ if (DEVID_9005_TYPE(device) == DEVID_9005_TYPE_HBA)
+ result = 1;
+ break;
+ case SUBID_9005_TYPE_RAID:
+ break;
+ default:
+ break;
+ }
+ }
+ return (result);
+}
+
+struct ahc_pci_identity *
+ahc_find_pci_device(ahc_dev_softc_t pci)
+{
+ uint64_t full_id;
+ uint16_t device;
+ uint16_t vendor;
+ uint16_t subdevice;
+ uint16_t subvendor;
+ struct ahc_pci_identity *entry;
+ u_int i;
+
+ vendor = ahc_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+ device = ahc_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+ subvendor = ahc_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+ subdevice = ahc_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+ full_id = ahc_compose_id(device,
+ vendor,
+ subdevice,
+ subvendor);
+
+ /*
+ * If the second function is not hooked up, ignore it.
+ * Unfortunately, not all MB vendors implement the
+ * subdevice ID as per the Adaptec spec, so do our best
+ * to sanity check it prior to accepting the subdevice
+ * ID as valid.
+ */
+ if (ahc_get_pci_function(pci) > 0
+ && ahc_9005_subdevinfo_valid(vendor, device, subvendor, subdevice)
+ && SUBID_9005_MFUNCENB(subdevice) == 0)
+ return (NULL);
+
+ for (i = 0; i < ahc_num_pci_devs; i++) {
+ entry = &ahc_pci_ident_table[i];
+ if (entry->full_id == (full_id & entry->id_mask)) {
+ /* Honor exclusion entries. */
+ if (entry->name == NULL)
+ return (NULL);
+ return (entry);
+ }
+ }
+ return (NULL);
+}
+
+int
+ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry)
+{
+ struct scb_data *shared_scb_data;
+ u_long l;
+ u_long s;
+ u_int command;
+ u_int our_id = 0;
+ u_int sxfrctl1;
+ u_int scsiseq;
+ u_int dscommand0;
+ int error;
+ uint8_t sblkctl;
+
+ shared_scb_data = NULL;
+ error = entry->setup(ahc);
+ if (error != 0)
+ return (error);
+ ahc->chip |= AHC_PCI;
+ ahc->description = entry->name;
+
+ ahc_power_state_change(ahc, AHC_POWER_STATE_D0);
+
+ error = ahc_pci_map_registers(ahc);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Before we continue probing the card, ensure that
+ * its interrupts are *disabled*. We don't want
+ * a misstep to hang the machine in an interrupt
+ * storm.
+ */
+ ahc_intr_enable(ahc, FALSE);
+
+ /*
+ * If we need to support high memory, enable dual
+ * address cycles. This bit must be set to enable
+ * high address bit generation even if we are on a
+ * 64bit bus (PCI64BIT set in devconfig).
+ */
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ uint32_t devconfig;
+
+ if (bootverbose)
+ printf("%s: Enabling 39Bit Addressing\n",
+ ahc_name(ahc));
+ devconfig = ahc_pci_read_config(ahc->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ devconfig |= DACEN;
+ ahc_pci_write_config(ahc->dev_softc, DEVCONFIG,
+ devconfig, /*bytes*/4);
+ }
+
+ /* Ensure busmastering is enabled */
+ command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1);
+ command |= PCIM_CMD_BUSMASTEREN;
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, /*bytes*/1);
+
+ /* On all PCI adapters, we allow SCB paging */
+ ahc->flags |= AHC_PAGESCBS;
+
+ error = ahc_softc_init(ahc);
+ if (error != 0)
+ return (error);
+
+ ahc->bus_intr = ahc_pci_intr;
+
+ /* Remeber how the card was setup in case there is no SEEPROM */
+ if ((ahc_inb(ahc, HCNTRL) & POWRDN) == 0) {
+ ahc_pause(ahc);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+ else
+ our_id = ahc_inb(ahc, SCSIID) & OID;
+ sxfrctl1 = ahc_inb(ahc, SXFRCTL1) & STPWEN;
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ } else {
+ sxfrctl1 = STPWEN;
+ our_id = 7;
+ scsiseq = 0;
+ }
+
+ error = ahc_reset(ahc);
+ if (error != 0)
+ return (ENXIO);
+
+ if ((ahc->features & AHC_DT) != 0) {
+ u_int sfunct;
+
+ /* Perform ALT-Mode Setup */
+ sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+ ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+ ahc_outb(ahc, OPTIONMODE,
+ OPTIONMODE_DEFAULTS|AUTOACKEN|BUSFREEREV|EXPPHASEDIS);
+ ahc_outb(ahc, SFUNCT, sfunct);
+
+ /* Normal mode setup */
+ ahc_outb(ahc, CRCCONTROL1, CRCVALCHKEN|CRCENDCHKEN|CRCREQCHKEN
+ |TARGCRCENDEN);
+ }
+
+ dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+ dscommand0 |= MPARCKEN|CACHETHEN;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+
+ /*
+ * DPARCKEN doesn't work correctly on
+ * some MBs so don't use it.
+ */
+ dscommand0 &= ~DPARCKEN;
+ }
+
+ /*
+ * Handle chips that must have cache line
+ * streaming (dis/en)abled.
+ */
+ if ((ahc->bugs & AHC_CACHETHEN_DIS_BUG) != 0)
+ dscommand0 |= CACHETHEN;
+
+ if ((ahc->bugs & AHC_CACHETHEN_BUG) != 0)
+ dscommand0 &= ~CACHETHEN;
+
+ ahc_outb(ahc, DSCOMMAND0, dscommand0);
+
+ ahc->pci_cachesize =
+ ahc_pci_read_config(ahc->dev_softc, CSIZE_LATTIME,
+ /*bytes*/1) & CACHESIZE;
+ ahc->pci_cachesize *= 4;
+
+ if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0
+ && ahc->pci_cachesize == 4) {
+
+ ahc_pci_write_config(ahc->dev_softc, CSIZE_LATTIME,
+ 0, /*bytes*/1);
+ ahc->pci_cachesize = 0;
+ }
+
+ /*
+ * We cannot perform ULTRA speeds without the presense
+ * of the external precision resistor.
+ */
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ uint32_t devconfig;
+
+ devconfig = ahc_pci_read_config(ahc->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ if ((devconfig & REXTVALID) == 0)
+ ahc->features &= ~AHC_ULTRA;
+ }
+
+ /* See if we have a SEEPROM and perform auto-term */
+ check_extport(ahc, &sxfrctl1);
+
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ ahc_outb(ahc, SBLKCTL, (sblkctl & ~(DIAGLEDEN|DIAGLEDON)));
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ ahc_outb(ahc, DFF_THRSH, RD_DFTHRSH_MAX|WR_DFTHRSH_MAX);
+ } else {
+ ahc_outb(ahc, DSPCISTATUS, DFTHRSH_100);
+ }
+
+ if (ahc->flags & AHC_USEDEFAULTS) {
+ /*
+ * PCI Adapter default setup
+ * Should only be used if the adapter does not have
+ * a SEEPROM.
+ */
+ /* See if someone else set us up already */
+ if (scsiseq != 0) {
+ printf("%s: Using left over BIOS settings\n",
+ ahc_name(ahc));
+ ahc->flags &= ~AHC_USEDEFAULTS;
+ ahc->flags |= AHC_BIOS_ENABLED;
+ } else {
+ /*
+ * Assume only one connector and always turn
+ * on termination.
+ */
+ our_id = 0x07;
+ sxfrctl1 = STPWEN;
+ }
+ ahc_outb(ahc, SCSICONF, our_id|ENSPCHK|RESET_SCSI);
+
+ ahc->our_id = our_id;
+ }
+
+ /*
+ * Take a look to see if we have external SRAM.
+ * We currently do not attempt to use SRAM that is
+ * shared among multiple controllers.
+ */
+ ahc_probe_ext_scbram(ahc);
+
+ /*
+ * Record our termination setting for the
+ * generic initialization routine.
+ */
+ if ((sxfrctl1 & STPWEN) != 0)
+ ahc->flags |= AHC_TERM_ENB_A;
+
+ /* Core initialization */
+ error = ahc_init(ahc);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Allow interrupts now that we are completely setup.
+ */
+ error = ahc_pci_map_int(ahc);
+ if (error != 0)
+ return (error);
+
+ ahc_list_lock(&l);
+ /*
+ * Link this softc in with all other ahc instances.
+ */
+ ahc_softc_insert(ahc);
+
+ ahc_lock(ahc, &s);
+ ahc_intr_enable(ahc, TRUE);
+ ahc_unlock(ahc, &s);
+
+ ahc_list_unlock(&l);
+ return (0);
+}
+
+/*
+ * Test for the presense of external sram in an
+ * "unshared" configuration.
+ */
+static int
+ahc_ext_scbram_present(struct ahc_softc *ahc)
+{
+ u_int chip;
+ int ramps;
+ int single_user;
+ uint32_t devconfig;
+
+ chip = ahc->chip & AHC_CHIPID_MASK;
+ devconfig = ahc_pci_read_config(ahc->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ single_user = (devconfig & MPORTMODE) != 0;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ramps = (ahc_inb(ahc, DSCOMMAND0) & RAMPS) != 0;
+ else if (chip == AHC_AIC7895 || chip == AHC_AIC7895C)
+ /*
+ * External SCBRAM arbitration is flakey
+ * on these chips. Unfortunately this means
+ * we don't use the extra SCB ram space on the
+ * 3940AUW.
+ */
+ ramps = 0;
+ else if (chip >= AHC_AIC7870)
+ ramps = (devconfig & RAMPSM) != 0;
+ else
+ ramps = 0;
+
+ if (ramps && single_user)
+ return (1);
+ return (0);
+}
+
+/*
+ * Enable external scbram.
+ */
+static void
+ahc_scbram_config(struct ahc_softc *ahc, int enable, int pcheck,
+ int fast, int large)
+{
+ uint32_t devconfig;
+
+ if (ahc->features & AHC_MULTI_FUNC) {
+ /*
+ * Set the SCB Base addr (highest address bit)
+ * depending on which channel we are.
+ */
+ ahc_outb(ahc, SCBBADDR, ahc_get_pci_function(ahc->dev_softc));
+ }
+
+ ahc->flags &= ~AHC_LSCBS_ENABLED;
+ if (large)
+ ahc->flags |= AHC_LSCBS_ENABLED;
+ devconfig = ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int dscommand0;
+
+ dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+ if (enable)
+ dscommand0 &= ~INTSCBRAMSEL;
+ else
+ dscommand0 |= INTSCBRAMSEL;
+ if (large)
+ dscommand0 &= ~USCBSIZE32;
+ else
+ dscommand0 |= USCBSIZE32;
+ ahc_outb(ahc, DSCOMMAND0, dscommand0);
+ } else {
+ if (fast)
+ devconfig &= ~EXTSCBTIME;
+ else
+ devconfig |= EXTSCBTIME;
+ if (enable)
+ devconfig &= ~SCBRAMSEL;
+ else
+ devconfig |= SCBRAMSEL;
+ if (large)
+ devconfig &= ~SCBSIZE32;
+ else
+ devconfig |= SCBSIZE32;
+ }
+ if (pcheck)
+ devconfig |= EXTSCBPEN;
+ else
+ devconfig &= ~EXTSCBPEN;
+
+ ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+}
+
+/*
+ * Take a look to see if we have external SRAM.
+ * We currently do not attempt to use SRAM that is
+ * shared among multiple controllers.
+ */
+static void
+ahc_probe_ext_scbram(struct ahc_softc *ahc)
+{
+ int num_scbs;
+ int test_num_scbs;
+ int enable;
+ int pcheck;
+ int fast;
+ int large;
+
+ enable = FALSE;
+ pcheck = FALSE;
+ fast = FALSE;
+ large = FALSE;
+ num_scbs = 0;
+
+ if (ahc_ext_scbram_present(ahc) == 0)
+ goto done;
+
+ /*
+ * Probe for the best parameters to use.
+ */
+ ahc_scbram_config(ahc, /*enable*/TRUE, pcheck, fast, large);
+ num_scbs = ahc_probe_scbs(ahc);
+ if (num_scbs == 0) {
+ /* The SRAM wasn't really present. */
+ goto done;
+ }
+ enable = TRUE;
+
+ /*
+ * Clear any outstanding parity error
+ * and ensure that parity error reporting
+ * is enabled.
+ */
+ ahc_outb(ahc, SEQCTL, 0);
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+ /* Now see if we can do parity */
+ ahc_scbram_config(ahc, enable, /*pcheck*/TRUE, fast, large);
+ num_scbs = ahc_probe_scbs(ahc);
+ if ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+ || (ahc_inb(ahc, ERROR) & MPARERR) == 0)
+ pcheck = TRUE;
+
+ /* Clear any resulting parity error */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+ /* Now see if we can do fast timing */
+ ahc_scbram_config(ahc, enable, pcheck, /*fast*/TRUE, large);
+ test_num_scbs = ahc_probe_scbs(ahc);
+ if (test_num_scbs == num_scbs
+ && ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+ || (ahc_inb(ahc, ERROR) & MPARERR) == 0))
+ fast = TRUE;
+
+ /*
+ * See if we can use large SCBs and still maintain
+ * the same overall count of SCBs.
+ */
+ if ((ahc->features & AHC_LARGE_SCBS) != 0) {
+ ahc_scbram_config(ahc, enable, pcheck, fast, /*large*/TRUE);
+ test_num_scbs = ahc_probe_scbs(ahc);
+ if (test_num_scbs >= num_scbs) {
+ large = TRUE;
+ num_scbs = test_num_scbs;
+ if (num_scbs >= 64) {
+ /*
+ * We have enough space to move the
+ * "busy targets table" into SCB space
+ * and make it qualify all the way to the
+ * lun level.
+ */
+ ahc->flags |= AHC_SCB_BTT;
+ }
+ }
+ }
+done:
+ /*
+ * Disable parity error reporting until we
+ * can load instruction ram.
+ */
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS);
+ /* Clear any latched parity error */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+ if (bootverbose && enable) {
+ printf("%s: External SRAM, %s access%s, %dbytes/SCB\n",
+ ahc_name(ahc), fast ? "fast" : "slow",
+ pcheck ? ", parity checking enabled" : "",
+ large ? 64 : 32);
+ }
+ ahc_scbram_config(ahc, enable, pcheck, fast, large);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static void
+check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
+{
+ struct seeprom_descriptor sd;
+ struct seeprom_config *sc;
+ int have_seeprom;
+ int have_autoterm;
+
+ sd.sd_ahc = ahc;
+ sd.sd_control_offset = SEECTL;
+ sd.sd_status_offset = SEECTL;
+ sd.sd_dataout_offset = SEECTL;
+ sc = ahc->seep_config;
+
+ /*
+ * For some multi-channel devices, the c46 is simply too
+ * small to work. For the other controller types, we can
+ * get our information from either SEEPROM type. Set the
+ * type to start our probe with accordingly.
+ */
+ if (ahc->flags & AHC_LARGE_SEEPROM)
+ sd.sd_chip = C56_66;
+ else
+ sd.sd_chip = C46;
+
+ sd.sd_MS = SEEMS;
+ sd.sd_RDY = SEERDY;
+ sd.sd_CS = SEECS;
+ sd.sd_CK = SEECK;
+ sd.sd_DO = SEEDO;
+ sd.sd_DI = SEEDI;
+
+ have_seeprom = ahc_acquire_seeprom(ahc, &sd);
+ if (have_seeprom) {
+
+ if (bootverbose)
+ printf("%s: Reading SEEPROM...", ahc_name(ahc));
+
+ for (;;) {
+ u_int start_addr;
+
+ start_addr = 32 * (ahc->channel - 'A');
+
+ have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)sc,
+ start_addr,
+ sizeof(*sc)/2);
+
+ if (have_seeprom)
+ have_seeprom = ahc_verify_cksum(sc);
+
+ if (have_seeprom != 0 || sd.sd_chip == C56_66) {
+ if (bootverbose) {
+ if (have_seeprom == 0)
+ printf ("checksum error\n");
+ else
+ printf ("done.\n");
+ }
+ break;
+ }
+ sd.sd_chip = C56_66;
+ }
+ ahc_release_seeprom(&sd);
+ }
+
+ if (!have_seeprom) {
+ /*
+ * Pull scratch ram settings and treat them as
+ * if they are the contents of an seeprom if
+ * the 'ADPT' signature is found in SCB2.
+ * We manually compose the data as 16bit values
+ * to avoid endian issues.
+ */
+ ahc_outb(ahc, SCBPTR, 2);
+ if (ahc_inb(ahc, SCB_BASE) == 'A'
+ && ahc_inb(ahc, SCB_BASE + 1) == 'D'
+ && ahc_inb(ahc, SCB_BASE + 2) == 'P'
+ && ahc_inb(ahc, SCB_BASE + 3) == 'T') {
+ uint16_t *sc_data;
+ int i;
+
+ sc_data = (uint16_t *)sc;
+ for (i = 0; i < 32; i++) {
+ uint16_t val;
+ int j;
+
+ j = i * 2;
+ val = ahc_inb(ahc, SRAM_BASE + j)
+ | ahc_inb(ahc, SRAM_BASE + j + 1) << 8;
+ }
+ have_seeprom = ahc_verify_cksum(sc);
+ if (have_seeprom)
+ ahc->flags |= AHC_SCB_CONFIG_USED;
+ }
+ /*
+ * Clear any SCB parity errors in case this data and
+ * its associated parity was not initialized by the BIOS
+ */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+ }
+
+ if (!have_seeprom) {
+ if (bootverbose)
+ printf("%s: No SEEPROM available.\n", ahc_name(ahc));
+ ahc->flags |= AHC_USEDEFAULTS;
+ free(ahc->seep_config, M_DEVBUF);
+ ahc->seep_config = NULL;
+ sc = NULL;
+ } else {
+ ahc_parse_pci_eeprom(ahc, sc);
+ }
+
+ /*
+ * Cards that have the external logic necessary to talk to
+ * a SEEPROM, are almost certain to have the remaining logic
+ * necessary for auto-termination control. This assumption
+ * hasn't failed yet...
+ */
+ have_autoterm = have_seeprom;
+
+ /*
+ * Some low-cost chips have SEEPROM and auto-term control built
+ * in, instead of using a GAL. They can tell us directly
+ * if the termination logic is enabled.
+ */
+ if ((ahc->features & AHC_SPIOCAP) != 0) {
+ if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) == 0)
+ have_autoterm = FALSE;
+ }
+
+ if (have_autoterm) {
+ ahc_acquire_seeprom(ahc, &sd);
+ configure_termination(ahc, &sd, sc->adapter_control, sxfrctl1);
+ ahc_release_seeprom(&sd);
+ } else if (have_seeprom) {
+ *sxfrctl1 &= ~STPWEN;
+ if ((sc->adapter_control & CFSTERM) != 0)
+ *sxfrctl1 |= STPWEN;
+ if (bootverbose)
+ printf("%s: Low byte termination %sabled\n",
+ ahc_name(ahc),
+ (*sxfrctl1 & STPWEN) ? "en" : "dis");
+ }
+}
+
+static void
+ahc_parse_pci_eeprom(struct ahc_softc *ahc, struct seeprom_config *sc)
+{
+ /*
+ * Put the data we've collected down into SRAM
+ * where ahc_init will find it.
+ */
+ int i;
+ int max_targ = sc->max_targets & CFMAXTARG;
+ u_int scsi_conf;
+ uint16_t discenable;
+ uint16_t ultraenb;
+
+ discenable = 0;
+ ultraenb = 0;
+ if ((sc->adapter_control & CFULTRAEN) != 0) {
+ /*
+ * Determine if this adapter has a "newstyle"
+ * SEEPROM format.
+ */
+ for (i = 0; i < max_targ; i++) {
+ if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0) {
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < max_targ; i++) {
+ u_int scsirate;
+ uint16_t target_mask;
+
+ target_mask = 0x01 << i;
+ if (sc->device_flags[i] & CFDISC)
+ discenable |= target_mask;
+ if ((ahc->flags & AHC_NEWEEPROM_FMT) != 0) {
+ if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0)
+ ultraenb |= target_mask;
+ } else if ((sc->adapter_control & CFULTRAEN) != 0) {
+ ultraenb |= target_mask;
+ }
+ if ((sc->device_flags[i] & CFXFER) == 0x04
+ && (ultraenb & target_mask) != 0) {
+ /* Treat 10MHz as a non-ultra speed */
+ sc->device_flags[i] &= ~CFXFER;
+ ultraenb &= ~target_mask;
+ }
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int offset;
+
+ if (sc->device_flags[i] & CFSYNCH)
+ offset = MAX_OFFSET_ULTRA2;
+ else
+ offset = 0;
+ ahc_outb(ahc, TARG_OFFSET + i, offset);
+
+ /*
+ * The ultra enable bits contain the
+ * high bit of the ultra2 sync rate
+ * field.
+ */
+ scsirate = (sc->device_flags[i] & CFXFER)
+ | ((ultraenb & target_mask) ? 0x8 : 0x0);
+ if (sc->device_flags[i] & CFWIDEB)
+ scsirate |= WIDEXFER;
+ } else {
+ scsirate = (sc->device_flags[i] & CFXFER) << 4;
+ if (sc->device_flags[i] & CFSYNCH)
+ scsirate |= SOFS;
+ if (sc->device_flags[i] & CFWIDEB)
+ scsirate |= WIDEXFER;
+ }
+ ahc_outb(ahc, TARG_SCSIRATE + i, scsirate);
+ }
+ ahc->our_id = sc->brtime_id & CFSCSIID;
+
+ scsi_conf = (ahc->our_id & 0x7);
+ if (sc->adapter_control & CFSPARITY)
+ scsi_conf |= ENSPCHK;
+ if (sc->adapter_control & CFRESETB)
+ scsi_conf |= RESET_SCSI;
+
+ ahc->flags |= (sc->adapter_control & CFBOOTCHAN) >> CFBOOTCHANSHIFT;
+
+ if (sc->bios_control & CFEXTEND)
+ ahc->flags |= AHC_EXTENDED_TRANS_A;
+
+ if (sc->bios_control & CFBIOSEN)
+ ahc->flags |= AHC_BIOS_ENABLED;
+ if (ahc->features & AHC_ULTRA
+ && (ahc->flags & AHC_NEWEEPROM_FMT) == 0) {
+ /* Should we enable Ultra mode? */
+ if (!(sc->adapter_control & CFULTRAEN))
+ /* Treat us as a non-ultra card */
+ ultraenb = 0;
+ }
+
+ if (sc->signature == CFSIGNATURE
+ || sc->signature == CFSIGNATURE2) {
+ uint32_t devconfig;
+
+ /* Honor the STPWLEVEL settings */
+ devconfig = ahc_pci_read_config(ahc->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ devconfig &= ~STPWLEVEL;
+ if ((sc->bios_control & CFSTPWLEVEL) != 0)
+ devconfig |= STPWLEVEL;
+ ahc_pci_write_config(ahc->dev_softc, DEVCONFIG,
+ devconfig, /*bytes*/4);
+ }
+ /* Set SCSICONF info */
+ ahc_outb(ahc, SCSICONF, scsi_conf);
+ ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+ ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+ ahc_outb(ahc, ULTRA_ENB, ultraenb & 0xff);
+ ahc_outb(ahc, ULTRA_ENB + 1, (ultraenb >> 8) & 0xff);
+}
+
+static void
+configure_termination(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd,
+ u_int adapter_control,
+ u_int *sxfrctl1)
+{
+ uint8_t brddat;
+
+ brddat = 0;
+
+ /*
+ * Update the settings in sxfrctl1 to match the
+ * termination settings
+ */
+ *sxfrctl1 = 0;
+
+ /*
+ * SEECS must be on for the GALS to latch
+ * the data properly. Be sure to leave MS
+ * on or we will release the seeprom.
+ */
+ SEEPROM_OUTB(sd, sd->sd_MS | sd->sd_CS);
+ if ((adapter_control & CFAUTOTERM) != 0
+ || (ahc->features & AHC_NEW_TERMCTL) != 0) {
+ int internal50_present;
+ int internal68_present;
+ int externalcable_present;
+ int eeprom_present;
+ int enableSEC_low;
+ int enableSEC_high;
+ int enablePRI_low;
+ int enablePRI_high;
+ int sum;
+
+ enableSEC_low = 0;
+ enableSEC_high = 0;
+ enablePRI_low = 0;
+ enablePRI_high = 0;
+ if ((ahc->features & AHC_NEW_TERMCTL) != 0) {
+ ahc_new_term_detect(ahc, &enableSEC_low,
+ &enableSEC_high,
+ &enablePRI_low,
+ &enablePRI_high,
+ &eeprom_present);
+ if ((adapter_control & CFSEAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual SE Termination\n",
+ ahc_name(ahc));
+ enableSEC_low = (adapter_control & CFSELOWTERM);
+ enableSEC_high =
+ (adapter_control & CFSEHIGHTERM);
+ }
+ if ((adapter_control & CFAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual LVD Termination\n",
+ ahc_name(ahc));
+ enablePRI_low = (adapter_control & CFSTERM);
+ enablePRI_high = (adapter_control & CFWSTERM);
+ }
+ /* Make the table calculations below happy */
+ internal50_present = 0;
+ internal68_present = 1;
+ externalcable_present = 1;
+ } else if ((ahc->features & AHC_SPIOCAP) != 0) {
+ aic785X_cable_detect(ahc, &internal50_present,
+ &externalcable_present,
+ &eeprom_present);
+ } else {
+ aic787X_cable_detect(ahc, &internal50_present,
+ &internal68_present,
+ &externalcable_present,
+ &eeprom_present);
+ }
+
+ if ((ahc->features & AHC_WIDE) == 0)
+ internal68_present = 0;
+
+ if (bootverbose
+ && (ahc->features & AHC_ULTRA2) == 0) {
+ printf("%s: internal 50 cable %s present",
+ ahc_name(ahc),
+ internal50_present ? "is":"not");
+
+ if ((ahc->features & AHC_WIDE) != 0)
+ printf(", internal 68 cable %s present",
+ internal68_present ? "is":"not");
+ printf("\n%s: external cable %s present\n",
+ ahc_name(ahc),
+ externalcable_present ? "is":"not");
+ }
+ if (bootverbose)
+ printf("%s: BIOS eeprom %s present\n",
+ ahc_name(ahc), eeprom_present ? "is" : "not");
+
+ if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) {
+ /*
+ * The 50 pin connector is a separate bus,
+ * so force it to always be terminated.
+ * In the future, perform current sensing
+ * to determine if we are in the middle of
+ * a properly terminated bus.
+ */
+ internal50_present = 0;
+ }
+
+ /*
+ * Now set the termination based on what
+ * we found.
+ * Flash Enable = BRDDAT7
+ * Secondary High Term Enable = BRDDAT6
+ * Secondary Low Term Enable = BRDDAT5 (7890)
+ * Primary High Term Enable = BRDDAT4 (7890)
+ */
+ if ((ahc->features & AHC_ULTRA2) == 0
+ && (internal50_present != 0)
+ && (internal68_present != 0)
+ && (externalcable_present != 0)) {
+ printf("%s: Illegal cable configuration!!. "
+ "Only two connectors on the "
+ "adapter may be used at a "
+ "time!\n", ahc_name(ahc));
+
+ /*
+ * Pretend there are no cables in the hope
+ * that having all of the termination on
+ * gives us a more stable bus.
+ */
+ internal50_present = 0;
+ internal68_present = 0;
+ externalcable_present = 0;
+ }
+
+ if ((ahc->features & AHC_WIDE) != 0
+ && ((externalcable_present == 0)
+ || (internal68_present == 0)
+ || (enableSEC_high != 0))) {
+ brddat |= BRDDAT6;
+ if (bootverbose) {
+ if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+ printf("%s: 68 pin termination "
+ "Enabled\n", ahc_name(ahc));
+ else
+ printf("%s: %sHigh byte termination "
+ "Enabled\n", ahc_name(ahc),
+ enableSEC_high ? "Secondary "
+ : "");
+ }
+ }
+
+ sum = internal50_present + internal68_present
+ + externalcable_present;
+ if (sum < 2 || (enableSEC_low != 0)) {
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ brddat |= BRDDAT5;
+ else
+ *sxfrctl1 |= STPWEN;
+ if (bootverbose) {
+ if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+ printf("%s: 50 pin termination "
+ "Enabled\n", ahc_name(ahc));
+ else
+ printf("%s: %sLow byte termination "
+ "Enabled\n", ahc_name(ahc),
+ enableSEC_low ? "Secondary "
+ : "");
+ }
+ }
+
+ if (enablePRI_low != 0) {
+ *sxfrctl1 |= STPWEN;
+ if (bootverbose)
+ printf("%s: Primary Low Byte termination "
+ "Enabled\n", ahc_name(ahc));
+ }
+
+ /*
+ * Setup STPWEN before setting up the rest of
+ * the termination per the tech note on the U160 cards.
+ */
+ ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+ if (enablePRI_high != 0) {
+ brddat |= BRDDAT4;
+ if (bootverbose)
+ printf("%s: Primary High Byte "
+ "termination Enabled\n",
+ ahc_name(ahc));
+ }
+
+ write_brdctl(ahc, brddat);
+
+ } else {
+ if ((adapter_control & CFSTERM) != 0) {
+ *sxfrctl1 |= STPWEN;
+
+ if (bootverbose)
+ printf("%s: %sLow byte termination Enabled\n",
+ ahc_name(ahc),
+ (ahc->features & AHC_ULTRA2) ? "Primary "
+ : "");
+ }
+
+ if ((adapter_control & CFWSTERM) != 0
+ && (ahc->features & AHC_WIDE) != 0) {
+ brddat |= BRDDAT6;
+ if (bootverbose)
+ printf("%s: %sHigh byte termination Enabled\n",
+ ahc_name(ahc),
+ (ahc->features & AHC_ULTRA2)
+ ? "Secondary " : "");
+ }
+
+ /*
+ * Setup STPWEN before setting up the rest of
+ * the termination per the tech note on the U160 cards.
+ */
+ ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+ if ((ahc->features & AHC_WIDE) != 0)
+ write_brdctl(ahc, brddat);
+ }
+ SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */
+}
+
+static void
+ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low,
+ int *enableSEC_high, int *enablePRI_low,
+ int *enablePRI_high, int *eeprom_present)
+{
+ uint8_t brdctl;
+
+ /*
+ * BRDDAT7 = Eeprom
+ * BRDDAT6 = Enable Secondary High Byte termination
+ * BRDDAT5 = Enable Secondary Low Byte termination
+ * BRDDAT4 = Enable Primary high byte termination
+ * BRDDAT3 = Enable Primary low byte termination
+ */
+ brdctl = read_brdctl(ahc);
+ *eeprom_present = brdctl & BRDDAT7;
+ *enableSEC_high = (brdctl & BRDDAT6);
+ *enableSEC_low = (brdctl & BRDDAT5);
+ *enablePRI_high = (brdctl & BRDDAT4);
+ *enablePRI_low = (brdctl & BRDDAT3);
+}
+
+static void
+aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *internal68_present, int *externalcable_present,
+ int *eeprom_present)
+{
+ uint8_t brdctl;
+
+ /*
+ * First read the status of our cables.
+ * Set the rom bank to 0 since the
+ * bank setting serves as a multiplexor
+ * for the cable detection logic.
+ * BRDDAT5 controls the bank switch.
+ */
+ write_brdctl(ahc, 0);
+
+ /*
+ * Now read the state of the internal
+ * connectors. BRDDAT6 is INT50 and
+ * BRDDAT7 is INT68.
+ */
+ brdctl = read_brdctl(ahc);
+ *internal50_present = (brdctl & BRDDAT6) ? 0 : 1;
+ *internal68_present = (brdctl & BRDDAT7) ? 0 : 1;
+
+ /*
+ * Set the rom bank to 1 and determine
+ * the other signals.
+ */
+ write_brdctl(ahc, BRDDAT5);
+
+ /*
+ * Now read the state of the external
+ * connectors. BRDDAT6 is EXT68 and
+ * BRDDAT7 is EPROMPS.
+ */
+ brdctl = read_brdctl(ahc);
+ *externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+ *eeprom_present = (brdctl & BRDDAT7) ? 1 : 0;
+}
+
+static void
+aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *externalcable_present, int *eeprom_present)
+{
+ uint8_t brdctl;
+ uint8_t spiocap;
+
+ spiocap = ahc_inb(ahc, SPIOCAP);
+ spiocap &= ~SOFTCMDEN;
+ spiocap |= EXT_BRDCTL;
+ ahc_outb(ahc, SPIOCAP, spiocap);
+ ahc_outb(ahc, BRDCTL, BRDRW|BRDCS);
+ ahc_outb(ahc, BRDCTL, 0);
+ brdctl = ahc_inb(ahc, BRDCTL);
+ *internal50_present = (brdctl & BRDDAT5) ? 0 : 1;
+ *externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+
+ *eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) ? 1 : 0;
+}
+
+int
+ahc_acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
+{
+ int wait;
+
+ if ((ahc->features & AHC_SPIOCAP) != 0
+ && (ahc_inb(ahc, SPIOCAP) & SEEPROM) == 0)
+ return (0);
+
+ /*
+ * Request access of the memory port. When access is
+ * granted, SEERDY will go high. We use a 1 second
+ * timeout which should be near 1 second more than
+ * is needed. Reason: after the chip reset, there
+ * should be no contention.
+ */
+ SEEPROM_OUTB(sd, sd->sd_MS);
+ wait = 1000; /* 1 second timeout in msec */
+ while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) {
+ ahc_delay(1000); /* delay 1 msec */
+ }
+ if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) {
+ SEEPROM_OUTB(sd, 0);
+ return (0);
+ }
+ return(1);
+}
+
+void
+ahc_release_seeprom(struct seeprom_descriptor *sd)
+{
+ /* Release access to the memory port and the serial EEPROM. */
+ SEEPROM_OUTB(sd, 0);
+}
+
+static void
+write_brdctl(struct ahc_softc *ahc, uint8_t value)
+{
+ uint8_t brdctl;
+
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+ brdctl = BRDSTB;
+ if (ahc->channel == 'B')
+ brdctl |= BRDCS;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ brdctl = 0;
+ } else {
+ brdctl = BRDSTB|BRDCS;
+ }
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_flush_device_writes(ahc);
+ brdctl |= value;
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_flush_device_writes(ahc);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ brdctl |= BRDSTB_ULTRA2;
+ else
+ brdctl &= ~BRDSTB;
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_flush_device_writes(ahc);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ brdctl = 0;
+ else
+ brdctl &= ~BRDCS;
+ ahc_outb(ahc, BRDCTL, brdctl);
+}
+
+static uint8_t
+read_brdctl(ahc)
+ struct ahc_softc *ahc;
+{
+ uint8_t brdctl;
+ uint8_t value;
+
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+ brdctl = BRDRW;
+ if (ahc->channel == 'B')
+ brdctl |= BRDCS;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ brdctl = BRDRW_ULTRA2;
+ } else {
+ brdctl = BRDRW|BRDCS;
+ }
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_flush_device_writes(ahc);
+ value = ahc_inb(ahc, BRDCTL);
+ ahc_outb(ahc, BRDCTL, 0);
+ return (value);
+}
+
+#define DPE 0x80
+#define SSE 0x40
+#define RMA 0x20
+#define RTA 0x10
+#define STA 0x08
+#define DPR 0x01
+
+void
+ahc_pci_intr(struct ahc_softc *ahc)
+{
+ u_int error;
+ u_int status1;
+
+ error = ahc_inb(ahc, ERROR);
+ if ((error & PCIERRSTAT) == 0)
+ return;
+
+ status1 = ahc_pci_read_config(ahc->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+
+ printf("%s: PCI error Interrupt at seqaddr = 0x%x\n",
+ ahc_name(ahc),
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+
+ if (status1 & DPE) {
+ printf("%s: Data Parity Error Detected during address "
+ "or write data phase\n", ahc_name(ahc));
+ }
+ if (status1 & SSE) {
+ printf("%s: Signal System Error Detected\n", ahc_name(ahc));
+ }
+ if (status1 & RMA) {
+ printf("%s: Received a Master Abort\n", ahc_name(ahc));
+ }
+ if (status1 & RTA) {
+ printf("%s: Received a Target Abort\n", ahc_name(ahc));
+ }
+ if (status1 & STA) {
+ printf("%s: Signaled a Target Abort\n", ahc_name(ahc));
+ }
+ if (status1 & DPR) {
+ printf("%s: Data Parity Error has been reported via PERR#\n",
+ ahc_name(ahc));
+ }
+
+ /* Clear latched errors. */
+ ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+ status1, /*bytes*/1);
+
+ if ((status1 & (DPE|SSE|RMA|RTA|STA|DPR)) == 0) {
+ printf("%s: Latched PCIERR interrupt with "
+ "no status bits set\n", ahc_name(ahc));
+ } else {
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ }
+
+ ahc_unpause(ahc);
+}
+
+static int
+ahc_aic785X_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7850;
+ ahc->features = AHC_AIC7850_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 1)
+ ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7860_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7860;
+ ahc->features = AHC_AIC7860_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 1)
+ ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+ return (0);
+}
+
+static int
+ahc_apa1480_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ int error;
+
+ pci = ahc->dev_softc;
+ error = ahc_aic7860_setup(ahc);
+ if (error != 0)
+ return (error);
+ ahc->features |= AHC_REMOVABLE;
+ return (0);
+}
+
+static int
+ahc_aic7870_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7870;
+ ahc->features = AHC_AIC7870_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+ return (0);
+}
+
+static int
+ahc_aha394X_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7870_setup(ahc);
+ if (error == 0)
+ error = ahc_aha394XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aha398X_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7870_setup(ahc);
+ if (error == 0)
+ error = ahc_aha398XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aha494X_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7870_setup(ahc);
+ if (error == 0)
+ error = ahc_aha494XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aic7880_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7880;
+ ahc->features = AHC_AIC7880_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 1) {
+ ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+ } else {
+ ahc->bugs |= AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+ }
+ return (0);
+}
+
+static int
+ahc_aha2940Pro_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ int error;
+
+ pci = ahc->dev_softc;
+ ahc->flags |= AHC_INT50_SPEEDFLEX;
+ error = ahc_aic7880_setup(ahc);
+ return (0);
+}
+
+static int
+ahc_aha394XU_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7880_setup(ahc);
+ if (error == 0)
+ error = ahc_aha394XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aha398XU_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7880_setup(ahc);
+ if (error == 0)
+ error = ahc_aha398XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aic7890_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7890;
+ ahc->features = AHC_AIC7890_FE;
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev == 0)
+ ahc->bugs |= AHC_AUTOFLUSH_BUG|AHC_CACHETHEN_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7892_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7892;
+ ahc->features = AHC_AIC7892_FE;
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ ahc->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7895_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+ /*
+ * The 'C' revision of the aic7895 has a few additional features.
+ */
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 4) {
+ ahc->chip = AHC_AIC7895C;
+ ahc->features = AHC_AIC7895C_FE;
+ } else {
+ u_int command;
+
+ ahc->chip = AHC_AIC7895;
+ ahc->features = AHC_AIC7895_FE;
+
+ /*
+ * The BIOS disables the use of MWI transactions
+ * since it does not have the MWI bug work around
+ * we have. Disabling MWI reduces performance, so
+ * turn it on again.
+ */
+ command = ahc_pci_read_config(pci, PCIR_COMMAND, /*bytes*/1);
+ command |= PCIM_CMD_MWRICEN;
+ ahc_pci_write_config(pci, PCIR_COMMAND, command, /*bytes*/1);
+ ahc->bugs |= AHC_PCI_MWI_BUG;
+ }
+ /*
+ * XXX Does CACHETHEN really not work??? What about PCI retry?
+ * on C level chips. Need to test, but for now, play it safe.
+ */
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_PCI_2_1_RETRY_BUG
+ | AHC_CACHETHEN_BUG;
+
+#if 0
+ uint32_t devconfig;
+
+ /*
+ * Cachesize must also be zero due to stray DAC
+ * problem when sitting behind some bridges.
+ */
+ ahc_pci_write_config(pci, CSIZE_LATTIME, 0, /*bytes*/1);
+ devconfig = ahc_pci_read_config(pci, DEVCONFIG, /*bytes*/1);
+ devconfig |= MRDCEN;
+ ahc_pci_write_config(pci, DEVCONFIG, devconfig, /*bytes*/1);
+#endif
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ return (0);
+}
+
+static int
+ahc_aic7896_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+ ahc->chip = AHC_AIC7896;
+ ahc->features = AHC_AIC7896_FE;
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ ahc->bugs |= AHC_CACHETHEN_DIS_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7899_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+ ahc->chip = AHC_AIC7899;
+ ahc->features = AHC_AIC7899_FE;
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ ahc->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+ return (0);
+}
+
+static int
+ahc_aha29160C_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7899_setup(ahc);
+ if (error != 0)
+ return (error);
+ ahc->features |= AHC_REMOVABLE;
+ return (0);
+}
+
+static int
+ahc_raid_setup(struct ahc_softc *ahc)
+{
+ printf("RAID functionality unsupported\n");
+ return (ENXIO);
+}
+
+static int
+ahc_aha394XX_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ switch (ahc_get_pci_slot(pci)) {
+ case AHC_394X_SLOT_CHANNEL_A:
+ ahc->channel = 'A';
+ break;
+ case AHC_394X_SLOT_CHANNEL_B:
+ ahc->channel = 'B';
+ break;
+ default:
+ printf("adapter at unexpected slot %d\n"
+ "unable to map to a channel\n",
+ ahc_get_pci_slot(pci));
+ ahc->channel = 'A';
+ }
+ return (0);
+}
+
+static int
+ahc_aha398XX_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ switch (ahc_get_pci_slot(pci)) {
+ case AHC_398X_SLOT_CHANNEL_A:
+ ahc->channel = 'A';
+ break;
+ case AHC_398X_SLOT_CHANNEL_B:
+ ahc->channel = 'B';
+ break;
+ case AHC_398X_SLOT_CHANNEL_C:
+ ahc->channel = 'C';
+ break;
+ default:
+ printf("adapter at unexpected slot %d\n"
+ "unable to map to a channel\n",
+ ahc_get_pci_slot(pci));
+ ahc->channel = 'A';
+ break;
+ }
+ ahc->flags |= AHC_LARGE_SEEPROM;
+ return (0);
+}
+
+static int
+ahc_aha494XX_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ switch (ahc_get_pci_slot(pci)) {
+ case AHC_494X_SLOT_CHANNEL_A:
+ ahc->channel = 'A';
+ break;
+ case AHC_494X_SLOT_CHANNEL_B:
+ ahc->channel = 'B';
+ break;
+ case AHC_494X_SLOT_CHANNEL_C:
+ ahc->channel = 'C';
+ break;
+ case AHC_494X_SLOT_CHANNEL_D:
+ ahc->channel = 'D';
+ break;
+ default:
+ printf("adapter at unexpected slot %d\n"
+ "unable to map to a channel\n",
+ ahc_get_pci_slot(pci));
+ ahc->channel = 'A';
+ }
+ ahc->flags |= AHC_LARGE_SEEPROM;
+ return (0);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_proc.c b/xen/drivers/scsi/aic7xxx/aic7xxx_proc.c
new file mode 100644
index 0000000000..9c93f57ea1
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_proc.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
+ * sym driver.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_proc.c#19 $
+ */
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+
+static void copy_mem_info(struct info_str *info, char *data, int len);
+static int copy_info(struct info_str *info, char *fmt, ...);
+static u_int scsi_calc_syncsrate(u_int period_factor);
+static void ahc_dump_target_state(struct ahc_softc *ahc,
+ struct info_str *info,
+ u_int our_id, char channel,
+ u_int target_id, u_int target_offset);
+static void ahc_dump_device_state(struct info_str *info,
+ struct ahc_linux_device *dev);
+static int ahc_proc_write_seeprom(struct ahc_softc *ahc,
+ char *buffer, int length);
+
+static void
+copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->offset + info->length)
+ len = info->offset + info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+
+ if (info->pos < info->offset) {
+ off_t partial;
+
+ partial = info->offset - info->pos;
+ data += partial;
+ info->pos += partial;
+ len -= partial;
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer, data, len);
+ info->pos += len;
+ info->buffer += len;
+ }
+}
+
+static int
+copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[256];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return (len);
+}
+
+/*
+ * Table of syncrates that don't follow the "divisible by 4"
+ * rule. This table will be expanded in future SCSI specs.
+ */
+static struct {
+ u_int period_factor;
+ u_int period; /* in 10ths of ns */
+} scsi_syncrates[] = {
+ { 0x09, 125 }, /* FAST-80 */
+ { 0x0a, 250 }, /* FAST-40 40MHz */
+ { 0x0b, 303 }, /* FAST-40 33MHz */
+ { 0x0c, 500 } /* FAST-20 */
+};
+
+/*
+ * Return the frequency in kHz corresponding to the given
+ * sync period factor.
+ */
+static u_int
+scsi_calc_syncsrate(u_int period_factor)
+{
+ int i;
+ int num_syncrates;
+
+ num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]);
+ /* See if the period is in the "exception" table */
+ for (i = 0; i < num_syncrates; i++) {
+
+ if (period_factor == scsi_syncrates[i].period_factor) {
+ /* Period in kHz */
+ return (10000000 / scsi_syncrates[i].period);
+ }
+ }
+
+ /*
+ * Wasn't in the table, so use the standard
+ * 4 times conversion.
+ */
+ return (10000000 / (period_factor * 4 * 10));
+}
+
+void
+ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo)
+{
+ u_int speed;
+ u_int freq;
+ u_int mb;
+
+ speed = 3300;
+ freq = 0;
+ if (tinfo->offset != 0) {
+ freq = scsi_calc_syncsrate(tinfo->period);
+ speed = freq;
+ }
+ speed *= (0x01 << tinfo->width);
+ mb = speed / 1000;
+ if (mb > 0)
+ copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
+ else
+ copy_info(info, "%dKB/s transfers", speed);
+
+ if (freq != 0) {
+ copy_info(info, " (%d.%03dMHz%s, offset %d",
+ freq / 1000, freq % 1000,
+ (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+ ? " DT" : "", tinfo->offset);
+ }
+
+ if (tinfo->width > 0) {
+ if (freq != 0) {
+ copy_info(info, ", ");
+ } else {
+ copy_info(info, " (");
+ }
+ copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+ } else if (freq != 0) {
+ copy_info(info, ")");
+ }
+ copy_info(info, "\n");
+}
+
+static void
+ahc_dump_target_state(struct ahc_softc *ahc, struct info_str *info,
+ u_int our_id, char channel, u_int target_id,
+ u_int target_offset)
+{
+ struct ahc_linux_target *targ;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ int lun;
+
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+ target_id, &tstate);
+ copy_info(info, "Channel %c Target %d Negotiation Settings\n",
+ channel, target_id);
+ copy_info(info, "\tUser: ");
+ ahc_format_transinfo(info, &tinfo->user);
+ targ = ahc->platform_data->targets[target_offset];
+ if (targ == NULL)
+ return;
+
+ copy_info(info, "\tGoal: ");
+ ahc_format_transinfo(info, &tinfo->goal);
+ copy_info(info, "\tCurr: ");
+ ahc_format_transinfo(info, &tinfo->curr);
+
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_linux_device *dev;
+
+ dev = targ->devices[lun];
+
+ if (dev == NULL)
+ continue;
+
+ ahc_dump_device_state(info, dev);
+ }
+}
+
+static void
+ahc_dump_device_state(struct info_str *info, struct ahc_linux_device *dev)
+{
+ copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+ dev->target->channel + 'A', dev->target->target, dev->lun);
+
+ copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued);
+ copy_info(info, "\t\tCommands Active %d\n", dev->active);
+ copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
+ copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+ copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+}
+
+static int
+ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length)
+{
+ struct seeprom_descriptor sd;
+ int have_seeprom;
+ u_long s;
+ int paused;
+ int written;
+
+ /* Default to failure. */
+ written = -EINVAL;
+ ahc_lock(ahc, &s);
+ paused = ahc_is_paused(ahc);
+ if (!paused)
+ ahc_pause(ahc);
+
+ if (length != sizeof(struct seeprom_config)) {
+ printf("ahc_proc_write_seeprom: incorrect buffer size\n");
+ goto done;
+ }
+
+ have_seeprom = ahc_verify_cksum((struct seeprom_config*)buffer);
+ if (have_seeprom == 0) {
+ printf("ahc_proc_write_seeprom: cksum verification failed\n");
+ goto done;
+ }
+
+ sd.sd_ahc = ahc;
+ if ((ahc->chip & AHC_VL) != 0) {
+ sd.sd_control_offset = SEECTL_2840;
+ sd.sd_status_offset = STATUS_2840;
+ sd.sd_dataout_offset = STATUS_2840;
+ sd.sd_chip = C46;
+ sd.sd_MS = 0;
+ sd.sd_RDY = EEPROM_TF;
+ sd.sd_CS = CS_2840;
+ sd.sd_CK = CK_2840;
+ sd.sd_DO = DO_2840;
+ sd.sd_DI = DI_2840;
+ have_seeprom = TRUE;
+ } else {
+ sd.sd_control_offset = SEECTL;
+ sd.sd_status_offset = SEECTL;
+ sd.sd_dataout_offset = SEECTL;
+ if (ahc->flags & AHC_LARGE_SEEPROM)
+ sd.sd_chip = C56_66;
+ else
+ sd.sd_chip = C46;
+ sd.sd_MS = SEEMS;
+ sd.sd_RDY = SEERDY;
+ sd.sd_CS = SEECS;
+ sd.sd_CK = SEECK;
+ sd.sd_DO = SEEDO;
+ sd.sd_DI = SEEDI;
+ have_seeprom = ahc_acquire_seeprom(ahc, &sd);
+ }
+
+ if (!have_seeprom) {
+ printf("ahc_proc_write_seeprom: No Serial EEPROM\n");
+ goto done;
+ } else {
+ u_int start_addr;
+
+ if (ahc->seep_config == NULL) {
+ ahc->seep_config = malloc(sizeof(*ahc->seep_config),
+ M_DEVBUF, M_NOWAIT);
+ if (ahc->seep_config == NULL) {
+ printf("aic7xxx: Unable to allocate serial "
+ "eeprom buffer. Write failing\n");
+ goto done;
+ }
+ }
+ printf("aic7xxx: Writing Serial EEPROM\n");
+ start_addr = 32 * (ahc->channel - 'A');
+ ahc_write_seeprom(&sd, (u_int16_t *)buffer, start_addr,
+ sizeof(struct seeprom_config)/2);
+ ahc_read_seeprom(&sd, (uint16_t *)ahc->seep_config,
+ start_addr, sizeof(struct seeprom_config)/2);
+ if ((ahc->chip & AHC_VL) == 0)
+ ahc_release_seeprom(&sd);
+ written = length;
+ }
+
+done:
+ if (!paused)
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ return (written);
+}
+
+/*
+ * Return information to handle /proc support for the driver.
+ */
+int
+ahc_linux_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ struct ahc_softc *ahc;
+ struct info_str info;
+ char ahc_info[256];
+ u_long s;
+ u_int max_targ;
+ u_int i;
+ int retval;
+
+ retval = -EINVAL;
+ ahc_list_lock(&s);
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+ if (ahc->platform_data->host->host_no == hostno)
+ break;
+ }
+
+ if (ahc == NULL)
+ goto done;
+
+ /* Has data been written to the file? */
+ if (inout == TRUE) {
+ retval = ahc_proc_write_seeprom(ahc, buffer, length);
+ goto done;
+ }
+
+ if (start)
+ *start = buffer;
+
+ info.buffer = buffer;
+ info.length = length;
+ info.offset = offset;
+ info.pos = 0;
+
+ copy_info(&info, "Adaptec AIC7xxx driver version: %s\n",
+ AIC7XXX_DRIVER_VERSION);
+ ahc_controller_info(ahc, ahc_info);
+ copy_info(&info, "%s\n\n", ahc_info);
+
+ if (ahc->seep_config == NULL)
+ copy_info(&info, "No Serial EEPROM\n");
+ else {
+ copy_info(&info, "Serial EEPROM:\n");
+ for (i = 0; i < sizeof(*ahc->seep_config)/2; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ copy_info(&info, "\n");
+ }
+ copy_info(&info, "0x%.4x ",
+ ((uint16_t*)ahc->seep_config)[i]);
+ }
+ copy_info(&info, "\n");
+ }
+ copy_info(&info, "\n");
+
+ max_targ = 15;
+ if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+ max_targ = 7;
+
+ for (i = 0; i <= max_targ; i++) {
+ u_int our_id;
+ u_int target_id;
+ char channel;
+
+ channel = 'A';
+ our_id = ahc->our_id;
+ target_id = i;
+ if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+ channel = 'B';
+ our_id = ahc->our_id_b;
+ target_id = i % 8;
+ }
+
+ ahc_dump_target_state(ahc, &info, our_id,
+ channel, target_id, i);
+ }
+ retval = info.pos > info.offset ? info.pos - info.offset : 0;
+done:
+ ahc_list_unlock(&s);
+ return (retval);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_reg.h b/xen/drivers/scsi/aic7xxx/aic7xxx_reg.h
new file mode 100644
index 0000000000..a82c3ae7eb
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_reg.h
@@ -0,0 +1,722 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#43 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#30 $
+ */
+
+#define SCSISEQ 0x00
+#define TEMODE 0x80
+#define SCSIRSTO 0x01
+
+#define SXFRCTL0 0x01
+#define DFON 0x80
+#define DFPEXP 0x40
+#define FAST20 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define SCAMEN 0x04
+#define CLRCHN 0x02
+
+#define SXFRCTL1 0x02
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define STIMESEL 0x18
+#define ENSTIMER 0x04
+#define ACTNEGEN 0x02
+#define STPWEN 0x01
+
+#define SCSISIGO 0x03
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+#define SCSISIGI 0x03
+#define P_DATAIN_DT 0x60
+#define P_DATAOUT_DT 0x20
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+#define SCSIRATE 0x04
+#define WIDEXFER 0x80
+#define SXFR 0x70
+#define ENABLE_CRC 0x40
+#define SINGLE_EDGE 0x10
+#define SOFS 0x0f
+#define SXFR_ULTRA2 0x0f
+
+#define SCSIID 0x05
+#define SCSIOFFSET 0x05
+#define SOFS_ULTRA2 0x7f
+
+#define SCSIDATL 0x06
+
+#define SCSIDATH 0x07
+
+#define STCNT 0x08
+
+#define OPTIONMODE 0x08
+#define AUTORATEEN 0x80
+#define AUTOACKEN 0x40
+#define ATNMGMNTEN 0x20
+#define BUSFREEREV 0x10
+#define EXPPHASEDIS 0x08
+#define SCSIDATL_IMGEN 0x04
+#define OPTIONMODE_DEFAULTS 0x03
+#define AUTO_MSGOUT_DE 0x02
+#define DIS_MSGIN_DUALEDGE 0x01
+
+#define TARGCRCCNT 0x0a
+
+#define CLRSINT0 0x0b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRIOERR 0x08
+#define CLRSWRAP 0x08
+#define CLRSPIORDY 0x02
+
+#define SSTAT0 0x0b
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define SWRAP 0x08
+#define IOERR 0x08
+#define SDONE 0x04
+#define SPIORDY 0x02
+#define DMADONE 0x01
+
+#define CLRSINT1 0x0c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+#define SSTAT1 0x0c
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+#define SSTAT2 0x0d
+#define OVERRUN 0x80
+#define SHVALID 0x40
+#define SFCNT 0x1f
+#define EXP_ACTIVE 0x10
+#define CRCVALERR 0x08
+#define CRCENDERR 0x04
+#define CRCREQERR 0x02
+#define DUAL_EDGE_ERR 0x01
+
+#define SSTAT3 0x0e
+#define SCSICNT 0xf0
+#define U2OFFCNT 0x7f
+#define OFFCNT 0x0f
+
+#define SCSIID_ULTRA2 0x0f
+
+#define SIMODE0 0x10
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENIOERR 0x08
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+#define SIMODE1 0x11
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+#define SCSIBUSL 0x12
+
+#define SCSIBUSH 0x13
+
+#define SHADDR 0x14
+
+#define SELTIMER 0x18
+#define TARGIDIN 0x18
+#define STAGE6 0x20
+#define STAGE5 0x10
+#define STAGE4 0x08
+#define STAGE3 0x04
+#define STAGE2 0x02
+#define STAGE1 0x01
+
+#define SELID 0x19
+#define SELID_MASK 0xf0
+#define ONEBIT 0x08
+
+#define SCAMCTL 0x1a
+#define ENSCAMSELO 0x80
+#define CLRSCAMSELID 0x40
+#define ALTSTIM 0x20
+#define DFLTTID 0x10
+#define SCAMLVL 0x03
+
+#define TARGID 0x1b
+
+#define SPIOCAP 0x1b
+#define SOFT1 0x80
+#define SOFT0 0x40
+#define SOFTCMDEN 0x20
+#define EXT_BRDCTL 0x10
+#define SEEPROM 0x08
+#define EEPROM 0x04
+#define ROM 0x02
+#define SSPIOCPS 0x01
+
+#define BRDCTL 0x1d
+#define BRDDAT7 0x80
+#define BRDDAT6 0x40
+#define BRDDAT5 0x20
+#define BRDDAT4 0x10
+#define BRDSTB 0x10
+#define BRDDAT3 0x08
+#define BRDCS 0x08
+#define BRDDAT2 0x04
+#define BRDRW 0x04
+#define BRDRW_ULTRA2 0x02
+#define BRDCTL1 0x02
+#define BRDCTL0 0x01
+#define BRDSTB_ULTRA2 0x01
+
+#define SEECTL 0x1e
+#define EXTARBACK 0x80
+#define EXTARBREQ 0x40
+#define SEEMS 0x20
+#define SEERDY 0x10
+#define SEECS 0x08
+#define SEECK 0x04
+#define SEEDO 0x02
+#define SEEDI 0x01
+
+#define SBLKCTL 0x1f
+#define DIAGLEDEN 0x80
+#define DIAGLEDON 0x40
+#define AUTOFLUSHDIS 0x20
+#define ENAB40 0x08
+#define SELBUSB 0x08
+#define ENAB20 0x04
+#define SELWIDE 0x02
+#define XCVR 0x01
+
+#define BUSY_TARGETS 0x20
+#define TARG_SCSIRATE 0x20
+
+#define ULTRA_ENB 0x30
+#define CMDSIZE_TABLE 0x30
+
+#define DISC_DSB 0x32
+
+#define CMDSIZE_TABLE_TAIL 0x34
+
+#define MWI_RESIDUAL 0x38
+
+#define NEXT_QUEUED_SCB 0x39
+
+#define MSG_OUT 0x3a
+
+#define DMAPARAMS 0x3b
+#define PRELOADEN 0x80
+#define WIDEODD 0x40
+#define SCSIEN 0x20
+#define SDMAEN 0x10
+#define SDMAENACK 0x10
+#define HDMAEN 0x08
+#define HDMAENACK 0x08
+#define DIRECTION 0x04
+#define FIFOFLUSH 0x02
+#define FIFORESET 0x01
+
+#define SEQ_FLAGS 0x3c
+#define IDENTIFY_SEEN 0x80
+#define TARGET_CMD_IS_TAGGED 0x40
+#define DPHASE 0x20
+#define TARG_CMD_PENDING 0x10
+#define CMDPHASE_PENDING 0x08
+#define DPHASE_PENDING 0x04
+#define SPHASE_PENDING 0x02
+#define NO_DISCONNECT 0x01
+
+#define SAVED_SCSIID 0x3d
+
+#define SAVED_LUN 0x3e
+
+#define LASTPHASE 0x3f
+#define P_MESGIN 0xe0
+#define PHASE_MASK 0xe0
+#define P_STATUS 0xc0
+#define P_MESGOUT 0xa0
+#define P_COMMAND 0x80
+#define CDI 0x80
+#define P_DATAIN 0x40
+#define IOI 0x40
+#define MSGI 0x20
+#define P_BUSFREE 0x01
+#define P_DATAOUT 0x00
+
+#define WAITING_SCBH 0x40
+
+#define DISCONNECTED_SCBH 0x41
+
+#define FREE_SCBH 0x42
+
+#define COMPLETE_SCBH 0x43
+
+#define HSCB_ADDR 0x44
+
+#define SHARED_DATA_ADDR 0x48
+
+#define KERNEL_QINPOS 0x4c
+
+#define QINPOS 0x4d
+
+#define QOUTPOS 0x4e
+
+#define KERNEL_TQINPOS 0x4f
+
+#define TQINPOS 0x50
+
+#define ARG_1 0x51
+#define RETURN_1 0x51
+#define SEND_MSG 0x80
+#define SEND_SENSE 0x40
+#define SEND_REJ 0x20
+#define MSGOUT_PHASEMIS 0x10
+#define EXIT_MSG_LOOP 0x08
+#define CONT_MSG_LOOP 0x04
+#define CONT_TARG_SESSION 0x02
+
+#define ARG_2 0x52
+#define RETURN_2 0x52
+
+#define LAST_MSG 0x53
+
+#define SCSISEQ_TEMPLATE 0x54
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+
+#define DATA_COUNT_ODD 0x55
+
+#define HA_274_BIOSGLOBAL 0x56
+#define INITIATOR_TAG 0x56
+#define HA_274_EXTENDED_TRANS 0x01
+
+#define SEQ_FLAGS2 0x57
+#define TARGET_MSG_PENDING 0x02
+#define SCB_DMA 0x01
+
+#define SCSICONF 0x5a
+#define TERM_ENB 0x80
+#define RESET_SCSI 0x40
+#define ENSPCHK 0x20
+#define HWSCSIID 0x0f
+#define HSCSIID 0x07
+
+#define INTDEF 0x5c
+#define EDGE_TRIG 0x80
+#define VECTOR 0x0f
+
+#define HOSTCONF 0x5d
+
+#define HA_274_BIOSCTRL 0x5f
+#define BIOSDISABLED 0x30
+#define BIOSMODE 0x30
+#define CHANNEL_B_PRIMARY 0x08
+
+#define SEQCTL 0x60
+#define PERRORDIS 0x80
+#define PAUSEDIS 0x40
+#define FAILDIS 0x20
+#define FASTMODE 0x10
+#define BRKADRINTEN 0x08
+#define STEP 0x04
+#define SEQRESET 0x02
+#define LOADRAM 0x01
+
+#define SEQRAM 0x61
+
+#define SEQADDR0 0x62
+
+#define SEQADDR1 0x63
+#define SEQADDR1_MASK 0x01
+
+#define ACCUM 0x64
+
+#define SINDEX 0x65
+
+#define DINDEX 0x66
+
+#define ALLONES 0x69
+
+#define ALLZEROS 0x6a
+
+#define NONE 0x6a
+
+#define FLAGS 0x6b
+#define ZERO 0x02
+#define CARRY 0x01
+
+#define SINDIR 0x6c
+
+#define DINDIR 0x6d
+
+#define FUNCTION1 0x6e
+
+#define STACK 0x6f
+
+#define TARG_OFFSET 0x70
+
+#define SRAM_BASE 0x70
+
+#define BCTL 0x84
+#define ACE 0x08
+#define ENABLE 0x01
+
+#define DSCOMMAND0 0x84
+#define CACHETHEN 0x80
+#define DPARCKEN 0x40
+#define MPARCKEN 0x20
+#define EXTREQLCK 0x10
+#define INTSCBRAMSEL 0x08
+#define RAMPS 0x04
+#define USCBSIZE32 0x02
+#define CIOPARCKEN 0x01
+
+#define BUSTIME 0x85
+#define BOFF 0xf0
+#define BON 0x0f
+
+#define DSCOMMAND1 0x85
+#define DSLATT 0xfc
+#define HADDLDSEL1 0x02
+#define HADDLDSEL0 0x01
+
+#define BUSSPD 0x86
+#define DFTHRSH 0xc0
+#define DFTHRSH_75 0x80
+#define STBOFF 0x38
+#define STBON 0x07
+
+#define HS_MAILBOX 0x86
+#define HOST_MAILBOX 0xf0
+#define HOST_TQINPOS 0x80
+#define SEQ_MAILBOX 0x0f
+
+#define DSPCISTATUS 0x86
+#define DFTHRSH_100 0xc0
+
+#define HCNTRL 0x87
+#define POWRDN 0x40
+#define SWINT 0x10
+#define IRQMS 0x08
+#define PAUSE 0x04
+#define INTEN 0x02
+#define CHIPRST 0x01
+#define CHIPRSTACK 0x01
+
+#define HADDR 0x88
+
+#define HCNT 0x8c
+
+#define SCBPTR 0x90
+
+#define INTSTAT 0x91
+#define SEQINT_MASK 0xf1
+#define OUT_OF_RANGE 0xe1
+#define NO_FREE_SCB 0xd1
+#define SCB_MISMATCH 0xc1
+#define MISSED_BUSFREE 0xb1
+#define MKMSG_FAILED 0xa1
+#define DATA_OVERRUN 0x91
+#define PERR_DETECTED 0x81
+#define BAD_STATUS 0x71
+#define HOST_MSG_LOOP 0x61
+#define PDATA_REINIT 0x51
+#define IGN_WIDE_RES 0x41
+#define NO_MATCH 0x31
+#define NO_IDENT 0x21
+#define SEND_REJECT 0x11
+#define INT_PEND 0x0f
+#define BRKADRINT 0x08
+#define SCSIINT 0x04
+#define CMDCMPLT 0x02
+#define BAD_PHASE 0x01
+#define SEQINT 0x01
+
+#define CLRINT 0x92
+#define CLRPARERR 0x10
+#define CLRBRKADRINT 0x08
+#define CLRSCSIINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSEQINT 0x01
+
+#define ERROR 0x92
+#define CIOPARERR 0x80
+#define PCIERRSTAT 0x40
+#define MPARERR 0x20
+#define DPARERR 0x10
+#define SQPARERR 0x08
+#define ILLOPCODE 0x04
+#define ILLSADDR 0x02
+#define ILLHADDR 0x01
+
+#define DFCNTRL 0x93
+
+#define DFSTATUS 0x94
+#define PRELOAD_AVAIL 0x80
+#define DFCACHETH 0x40
+#define FIFOQWDEMP 0x20
+#define MREQPEND 0x10
+#define HDONE 0x08
+#define DFTHRESH 0x04
+#define FIFOFULL 0x02
+#define FIFOEMP 0x01
+
+#define DFWADDR 0x95
+
+#define DFRADDR 0x97
+
+#define DFDAT 0x99
+
+#define SCBCNT 0x9a
+#define SCBAUTO 0x80
+#define SCBCNT_MASK 0x1f
+
+#define QINFIFO 0x9b
+
+#define QINCNT 0x9c
+
+#define QOUTFIFO 0x9d
+
+#define CRCCONTROL1 0x9d
+#define CRCONSEEN 0x80
+#define CRCVALCHKEN 0x40
+#define CRCENDCHKEN 0x20
+#define CRCREQCHKEN 0x10
+#define TARGCRCENDEN 0x08
+#define TARGCRCCNTEN 0x04
+
+#define QOUTCNT 0x9e
+
+#define SCSIPHASE 0x9e
+#define STATUS_PHASE 0x20
+#define COMMAND_PHASE 0x10
+#define MSG_IN_PHASE 0x08
+#define MSG_OUT_PHASE 0x04
+#define DATA_PHASE_MASK 0x03
+#define DATA_IN_PHASE 0x02
+#define DATA_OUT_PHASE 0x01
+
+#define SFUNCT 0x9f
+#define ALT_MODE 0x80
+
+#define SCB_BASE 0xa0
+
+#define SCB_CDB_PTR 0xa0
+#define SCB_RESIDUAL_DATACNT 0xa0
+#define SCB_CDB_STORE 0xa0
+
+#define SCB_RESIDUAL_SGPTR 0xa4
+
+#define SCB_SCSI_STATUS 0xa8
+
+#define SCB_TARGET_PHASES 0xa9
+
+#define SCB_TARGET_DATA_DIR 0xaa
+
+#define SCB_TARGET_ITAG 0xab
+
+#define SCB_DATAPTR 0xac
+
+#define SCB_DATACNT 0xb0
+#define SG_LAST_SEG 0x80
+#define SG_HIGH_ADDR_BITS 0x7f
+
+#define SCB_SGPTR 0xb4
+#define SG_RESID_VALID 0x04
+#define SG_FULL_RESID 0x02
+#define SG_LIST_NULL 0x01
+
+#define SCB_CONTROL 0xb8
+#define TARGET_SCB 0x80
+#define DISCENB 0x40
+#define TAG_ENB 0x20
+#define MK_MESSAGE 0x10
+#define ULTRAENB 0x08
+#define DISCONNECTED 0x04
+#define SCB_TAG_TYPE 0x03
+
+#define SCB_SCSIID 0xb9
+#define TID 0xf0
+#define TWIN_CHNLB 0x80
+#define TWIN_TID 0x70
+#define OID 0x0f
+
+#define SCB_LUN 0xba
+#define LID 0xff
+
+#define SCB_TAG 0xbb
+
+#define SCB_CDB_LEN 0xbc
+
+#define SCB_SCSIRATE 0xbd
+
+#define SCB_SCSIOFFSET 0xbe
+
+#define SCB_NEXT 0xbf
+
+#define SCB_64_SPARE 0xc0
+
+#define SEECTL_2840 0xc0
+#define CS_2840 0x04
+#define CK_2840 0x02
+#define DO_2840 0x01
+
+#define STATUS_2840 0xc1
+#define EEPROM_TF 0x80
+#define BIOS_SEL 0x60
+#define ADSEL 0x1e
+#define DI_2840 0x01
+
+#define SCB_64_BTT 0xd0
+
+#define CCHADDR 0xe0
+
+#define CCHCNT 0xe8
+
+#define CCSGRAM 0xe9
+
+#define CCSGADDR 0xea
+
+#define CCSGCTL 0xeb
+#define CCSGDONE 0x80
+#define CCSGEN 0x08
+#define SG_FETCH_NEEDED 0x02
+#define CCSGRESET 0x01
+
+#define CCSCBRAM 0xec
+
+#define CCSCBADDR 0xed
+
+#define CCSCBCTL 0xee
+#define CCSCBDONE 0x80
+#define ARRDONE 0x40
+#define CCARREN 0x10
+#define CCSCBEN 0x08
+#define CCSCBDIR 0x04
+#define CCSCBRESET 0x01
+
+#define CCSCBCNT 0xef
+
+#define SCBBADDR 0xf0
+
+#define CCSCBPTR 0xf1
+
+#define HNSCB_QOFF 0xf4
+
+#define SNSCB_QOFF 0xf6
+
+#define SDSCB_QOFF 0xf8
+
+#define QOFF_CTLSTA 0xfa
+#define SCB_AVAIL 0x40
+#define SNSCB_ROLLOVER 0x20
+#define SDSCB_ROLLOVER 0x10
+#define SCB_QSIZE 0x07
+#define SCB_QSIZE_256 0x06
+
+#define DFF_THRSH 0xfb
+#define WR_DFTHRSH 0x70
+#define WR_DFTHRSH_MAX 0x70
+#define WR_DFTHRSH_90 0x60
+#define WR_DFTHRSH_85 0x50
+#define WR_DFTHRSH_75 0x40
+#define WR_DFTHRSH_63 0x30
+#define WR_DFTHRSH_50 0x20
+#define WR_DFTHRSH_25 0x10
+#define RD_DFTHRSH 0x07
+#define RD_DFTHRSH_MAX 0x07
+#define RD_DFTHRSH_90 0x06
+#define RD_DFTHRSH_85 0x05
+#define RD_DFTHRSH_75 0x04
+#define RD_DFTHRSH_63 0x03
+#define RD_DFTHRSH_50 0x02
+#define RD_DFTHRSH_25 0x01
+#define RD_DFTHRSH_MIN 0x00
+#define WR_DFTHRSH_MIN 0x00
+
+#define SG_CACHE_SHADOW 0xfc
+#define SG_ADDR_MASK 0xf8
+#define ODD_SEG 0x04
+#define LAST_SEG 0x02
+#define LAST_SEG_DONE 0x01
+
+#define SG_CACHE_PRE 0xfc
+
+
+#define MAX_OFFSET_ULTRA2 0x7f
+#define MAX_OFFSET_16BIT 0x08
+#define BUS_8_BIT 0x00
+#define TARGET_CMD_CMPLT 0xfe
+#define STATUS_QUEUE_FULL 0x28
+#define STATUS_BUSY 0x08
+#define MAX_OFFSET_8BIT 0x0f
+#define BUS_32_BIT 0x02
+#define CCSGADDR_MAX 0x80
+#define TID_SHIFT 0x04
+#define SCB_DOWNLOAD_SIZE_64 0x30
+#define HOST_MAILBOX_SHIFT 0x04
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define CCSGRAM_MAXSEGS 0x10
+#define SCB_LIST_NULL 0xff
+#define SG_SIZEOF 0x08
+#define SCB_DOWNLOAD_SIZE 0x20
+#define SEQ_MAILBOX_SHIFT 0x00
+#define TARGET_DATA_IN 0x01
+#define HOST_MSG 0xff
+#define BUS_16_BIT 0x01
+#define SCB_UPLOAD_SIZE 0x20
+
+
+/* Downloaded Constant Definitions */
+#define INVERTED_CACHESIZE_MASK 0x03
+#define SG_PREFETCH_ADDR_MASK 0x06
+#define SG_PREFETCH_ALIGN_MASK 0x05
+#define QOUTFIFO_OFFSET 0x00
+#define SG_PREFETCH_CNT 0x04
+#define CACHESIZE_MASK 0x02
+#define QINFIFO_OFFSET 0x01
+#define DOWNLOAD_CONST_COUNT 0x07
+
+
+/* Exported Labels */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_seq.h b/xen/drivers/scsi/aic7xxx/aic7xxx_seq.h
new file mode 100644
index 0000000000..a31de71a1b
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aic7xxx_seq.h
@@ -0,0 +1,1301 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#43 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#30 $
+ */
+static uint8_t seqprog[] = {
+ 0xb2, 0x00, 0x00, 0x08,
+ 0xf7, 0x11, 0x22, 0x08,
+ 0x00, 0x65, 0xe4, 0x59,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0xff, 0x6a, 0x24, 0x08,
+ 0x40, 0x00, 0x40, 0x68,
+ 0x08, 0x1f, 0x3e, 0x10,
+ 0x40, 0x00, 0x40, 0x68,
+ 0xff, 0x40, 0x3c, 0x60,
+ 0x08, 0x1f, 0x3e, 0x10,
+ 0x60, 0x0b, 0x42, 0x68,
+ 0x40, 0xfa, 0x12, 0x78,
+ 0x01, 0x4d, 0xc8, 0x30,
+ 0x00, 0x4c, 0x12, 0x70,
+ 0x01, 0x39, 0xa2, 0x30,
+ 0x00, 0x6a, 0xb6, 0x5e,
+ 0x01, 0x51, 0x20, 0x31,
+ 0x01, 0x57, 0xae, 0x00,
+ 0x0d, 0x6a, 0x76, 0x00,
+ 0x00, 0x51, 0x08, 0x5e,
+ 0x01, 0x51, 0xc8, 0x30,
+ 0x00, 0x39, 0xc8, 0x60,
+ 0x00, 0xbb, 0x30, 0x70,
+ 0xc1, 0x6a, 0xce, 0x5e,
+ 0x01, 0xbf, 0x72, 0x30,
+ 0x01, 0x40, 0x7e, 0x31,
+ 0x01, 0x90, 0x80, 0x30,
+ 0x01, 0xf6, 0xd4, 0x30,
+ 0x01, 0x4d, 0x9a, 0x18,
+ 0xfe, 0x57, 0xae, 0x08,
+ 0x01, 0x40, 0x20, 0x31,
+ 0x00, 0x65, 0xcc, 0x58,
+ 0x60, 0x0b, 0x40, 0x78,
+ 0x08, 0x6a, 0x18, 0x00,
+ 0x08, 0x11, 0x22, 0x00,
+ 0x60, 0x0b, 0x00, 0x78,
+ 0x40, 0x0b, 0xfc, 0x68,
+ 0x80, 0x0b, 0xb6, 0x78,
+ 0x20, 0x6a, 0x16, 0x00,
+ 0xa4, 0x6a, 0x06, 0x00,
+ 0x08, 0x3c, 0x78, 0x00,
+ 0x01, 0x50, 0xc8, 0x30,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x48, 0x6a, 0xf2, 0x5d,
+ 0x01, 0x6a, 0xdc, 0x01,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x48, 0x6a, 0xf2, 0x5d,
+ 0x01, 0x6a, 0x26, 0x01,
+ 0xf0, 0x19, 0x7a, 0x08,
+ 0x0f, 0x18, 0xc8, 0x08,
+ 0x0f, 0x0f, 0xc8, 0x08,
+ 0x0f, 0x05, 0xc8, 0x08,
+ 0x00, 0x3d, 0x7a, 0x00,
+ 0x08, 0x1f, 0x6e, 0x78,
+ 0x80, 0x3d, 0x7a, 0x00,
+ 0x01, 0x3d, 0xd8, 0x31,
+ 0x01, 0x3d, 0x32, 0x31,
+ 0x10, 0x03, 0x48, 0x79,
+ 0x00, 0x65, 0xf4, 0x58,
+ 0x80, 0x66, 0xae, 0x78,
+ 0x01, 0x66, 0xd8, 0x31,
+ 0x01, 0x66, 0x32, 0x31,
+ 0x3f, 0x66, 0x7c, 0x08,
+ 0x40, 0x66, 0x82, 0x68,
+ 0x01, 0x3c, 0x78, 0x00,
+ 0x10, 0x03, 0x9e, 0x78,
+ 0x00, 0x65, 0xf4, 0x58,
+ 0xe0, 0x66, 0xc8, 0x18,
+ 0x00, 0x65, 0xaa, 0x50,
+ 0xdd, 0x66, 0xc8, 0x18,
+ 0x00, 0x65, 0xaa, 0x48,
+ 0x01, 0x66, 0xd8, 0x31,
+ 0x01, 0x66, 0x32, 0x31,
+ 0x10, 0x03, 0x48, 0x79,
+ 0x00, 0x65, 0xf4, 0x58,
+ 0x01, 0x66, 0xd8, 0x31,
+ 0x01, 0x66, 0x32, 0x31,
+ 0x01, 0x66, 0xac, 0x30,
+ 0x40, 0x3c, 0x78, 0x00,
+ 0xff, 0x6a, 0xd8, 0x01,
+ 0xff, 0x6a, 0x32, 0x01,
+ 0x90, 0x3c, 0x78, 0x00,
+ 0x02, 0x57, 0x3c, 0x69,
+ 0x10, 0x03, 0x3a, 0x69,
+ 0x00, 0x65, 0x1e, 0x41,
+ 0x02, 0x57, 0xae, 0x00,
+ 0x00, 0x65, 0x9e, 0x40,
+ 0x61, 0x6a, 0xce, 0x5e,
+ 0x08, 0x51, 0x1e, 0x71,
+ 0x02, 0x0b, 0xb2, 0x78,
+ 0x00, 0x65, 0xae, 0x40,
+ 0x1a, 0x01, 0x02, 0x00,
+ 0xf0, 0x19, 0x7a, 0x08,
+ 0x0f, 0x0f, 0xc8, 0x08,
+ 0x0f, 0x05, 0xc8, 0x08,
+ 0x00, 0x3d, 0x7a, 0x00,
+ 0x08, 0x1f, 0xc4, 0x78,
+ 0x80, 0x3d, 0x7a, 0x00,
+ 0x20, 0x6a, 0x16, 0x00,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0x00, 0x65, 0xa8, 0x5e,
+ 0x00, 0x65, 0x12, 0x40,
+ 0x20, 0x11, 0xd2, 0x68,
+ 0x20, 0x6a, 0x18, 0x00,
+ 0x20, 0x11, 0x22, 0x00,
+ 0xf7, 0x1f, 0xca, 0x08,
+ 0x80, 0xb9, 0xd8, 0x78,
+ 0x08, 0x65, 0xca, 0x00,
+ 0x01, 0x65, 0x3e, 0x30,
+ 0x01, 0xb9, 0x1e, 0x30,
+ 0x7f, 0xb9, 0x0a, 0x08,
+ 0x01, 0xb9, 0x0a, 0x30,
+ 0x01, 0x54, 0xca, 0x30,
+ 0x80, 0xb8, 0xe6, 0x78,
+ 0x80, 0x65, 0xca, 0x00,
+ 0x01, 0x65, 0x00, 0x34,
+ 0x01, 0x54, 0x00, 0x34,
+ 0x1a, 0x01, 0x02, 0x00,
+ 0x08, 0xb8, 0xf0, 0x78,
+ 0x20, 0x01, 0x02, 0x00,
+ 0x02, 0xbd, 0x08, 0x34,
+ 0x01, 0xbd, 0x08, 0x34,
+ 0x08, 0x01, 0x02, 0x00,
+ 0x02, 0x0b, 0xf6, 0x78,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0x01, 0x06, 0xcc, 0x34,
+ 0xb2, 0x00, 0x00, 0x08,
+ 0x01, 0x40, 0x20, 0x31,
+ 0x01, 0xbf, 0x80, 0x30,
+ 0x01, 0xb9, 0x7a, 0x30,
+ 0x01, 0xba, 0x7c, 0x30,
+ 0x00, 0x65, 0xea, 0x58,
+ 0x80, 0x0b, 0xbe, 0x79,
+ 0xe4, 0x6a, 0x64, 0x5d,
+ 0x40, 0x6a, 0x16, 0x00,
+ 0x80, 0xba, 0x7a, 0x5d,
+ 0x20, 0xb8, 0x16, 0x79,
+ 0x20, 0x6a, 0x7a, 0x5d,
+ 0x00, 0xab, 0x7a, 0x5d,
+ 0x01, 0xa9, 0x78, 0x30,
+ 0x10, 0xb8, 0x1e, 0x79,
+ 0xe4, 0x6a, 0x64, 0x5d,
+ 0x00, 0x65, 0xae, 0x40,
+ 0x10, 0x03, 0x38, 0x69,
+ 0x08, 0x3c, 0x54, 0x69,
+ 0x04, 0x3c, 0x8c, 0x69,
+ 0x02, 0x3c, 0x92, 0x69,
+ 0x01, 0x3c, 0x3e, 0x79,
+ 0x01, 0x6a, 0xa2, 0x30,
+ 0x00, 0x65, 0x9e, 0x59,
+ 0x04, 0x51, 0x2c, 0x61,
+ 0x00, 0x6a, 0xb6, 0x5e,
+ 0x0d, 0x6a, 0x76, 0x00,
+ 0x00, 0xbb, 0x08, 0x5e,
+ 0x00, 0x65, 0xec, 0x58,
+ 0x00, 0x65, 0x16, 0x41,
+ 0xa4, 0x6a, 0x06, 0x00,
+ 0x00, 0x65, 0xf4, 0x58,
+ 0x00, 0x65, 0xae, 0x40,
+ 0xe4, 0x6a, 0x64, 0x5d,
+ 0x20, 0x3c, 0x44, 0x79,
+ 0x02, 0x6a, 0x7a, 0x5d,
+ 0x04, 0x6a, 0x7a, 0x5d,
+ 0x01, 0x03, 0x46, 0x69,
+ 0xf7, 0x11, 0x22, 0x08,
+ 0xff, 0x6a, 0x24, 0x08,
+ 0xff, 0x6a, 0x06, 0x08,
+ 0x01, 0x6a, 0x7e, 0x00,
+ 0x00, 0x65, 0x9e, 0x59,
+ 0x00, 0x65, 0x04, 0x40,
+ 0x80, 0x86, 0xc8, 0x08,
+ 0x01, 0x4f, 0xc8, 0x30,
+ 0x00, 0x50, 0x66, 0x61,
+ 0xc4, 0x6a, 0x64, 0x5d,
+ 0x40, 0x3c, 0x62, 0x79,
+ 0x28, 0x6a, 0x7a, 0x5d,
+ 0x00, 0x65, 0x46, 0x41,
+ 0x08, 0x6a, 0x7a, 0x5d,
+ 0x00, 0x65, 0x46, 0x41,
+ 0x84, 0x6a, 0x64, 0x5d,
+ 0x00, 0x65, 0xf4, 0x58,
+ 0x01, 0x66, 0xc8, 0x30,
+ 0x01, 0x64, 0xd8, 0x31,
+ 0x01, 0x64, 0x32, 0x31,
+ 0x5b, 0x64, 0xc8, 0x28,
+ 0x30, 0x64, 0xca, 0x18,
+ 0x01, 0x6c, 0xc8, 0x30,
+ 0xff, 0x64, 0x88, 0x79,
+ 0x08, 0x01, 0x02, 0x00,
+ 0x02, 0x0b, 0x7a, 0x79,
+ 0x01, 0x64, 0x80, 0x61,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0x01, 0x06, 0xd8, 0x31,
+ 0x01, 0x06, 0x32, 0x31,
+ 0xff, 0x64, 0xc8, 0x18,
+ 0xff, 0x64, 0x7a, 0x69,
+ 0xf7, 0x3c, 0x78, 0x08,
+ 0x00, 0x65, 0x1e, 0x41,
+ 0x40, 0xaa, 0x7e, 0x10,
+ 0x04, 0xaa, 0x64, 0x5d,
+ 0x00, 0x65, 0x56, 0x42,
+ 0xc4, 0x6a, 0x64, 0x5d,
+ 0xc0, 0x6a, 0x7e, 0x00,
+ 0x00, 0xa8, 0x7a, 0x5d,
+ 0xe4, 0x6a, 0x06, 0x00,
+ 0x00, 0x6a, 0x7a, 0x5d,
+ 0x00, 0x65, 0x46, 0x41,
+ 0x10, 0x3c, 0xa2, 0x69,
+ 0x00, 0xbb, 0x84, 0x44,
+ 0x18, 0x6a, 0xda, 0x01,
+ 0x01, 0x69, 0xd8, 0x31,
+ 0x1c, 0x6a, 0xd0, 0x01,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0xaa, 0x79,
+ 0xff, 0x6a, 0xdc, 0x09,
+ 0x01, 0x93, 0x26, 0x01,
+ 0x03, 0x6a, 0x2a, 0x01,
+ 0x01, 0x69, 0x32, 0x31,
+ 0x1c, 0x6a, 0xd6, 0x5d,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x9e, 0x5e,
+ 0x01, 0x50, 0xa0, 0x18,
+ 0x02, 0x6a, 0x22, 0x05,
+ 0x80, 0x6a, 0x74, 0x00,
+ 0x80, 0x3c, 0x78, 0x00,
+ 0x40, 0x6a, 0x16, 0x00,
+ 0x00, 0x65, 0xce, 0x5d,
+ 0x01, 0x3f, 0xc8, 0x30,
+ 0xbf, 0x64, 0x56, 0x7a,
+ 0x80, 0x64, 0xaa, 0x73,
+ 0xa0, 0x64, 0x08, 0x74,
+ 0xc0, 0x64, 0xfc, 0x73,
+ 0xe0, 0x64, 0x38, 0x74,
+ 0x01, 0x6a, 0xce, 0x5e,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0xf7, 0x11, 0x22, 0x08,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0xff, 0x6a, 0x24, 0x08,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0x09, 0x0c, 0xde, 0x79,
+ 0x08, 0x0c, 0x04, 0x68,
+ 0xb1, 0x6a, 0xce, 0x5e,
+ 0xff, 0x6a, 0x26, 0x09,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x02, 0x6a, 0x08, 0x30,
+ 0xff, 0x6a, 0x08, 0x08,
+ 0xdf, 0x01, 0x02, 0x08,
+ 0x01, 0x6a, 0x7e, 0x00,
+ 0xff, 0x6a, 0x78, 0x0c,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x08, 0xa4, 0x48, 0x19,
+ 0x00, 0xa5, 0x4a, 0x21,
+ 0x00, 0xa6, 0x4c, 0x21,
+ 0x00, 0xa7, 0x4e, 0x25,
+ 0x08, 0xeb, 0xd2, 0x7e,
+ 0x80, 0xeb, 0xfe, 0x79,
+ 0xff, 0x6a, 0xd6, 0x09,
+ 0x08, 0xeb, 0x02, 0x6a,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x80, 0xa3, 0xd2, 0x6e,
+ 0x88, 0xeb, 0x18, 0x72,
+ 0x08, 0xeb, 0xd2, 0x6e,
+ 0x04, 0xea, 0x1c, 0xe2,
+ 0x08, 0xee, 0xd2, 0x6e,
+ 0x04, 0x6a, 0xd0, 0x81,
+ 0x05, 0xa4, 0xc0, 0x89,
+ 0x03, 0xa5, 0xc2, 0x31,
+ 0x09, 0x6a, 0xd6, 0x05,
+ 0x00, 0x65, 0x00, 0x5a,
+ 0x06, 0xa4, 0xd4, 0x89,
+ 0x80, 0x94, 0xd2, 0x7e,
+ 0x07, 0xe9, 0x10, 0x31,
+ 0x01, 0x8c, 0x24, 0x7a,
+ 0x01, 0x55, 0xaa, 0x10,
+ 0x01, 0xe9, 0x46, 0x31,
+ 0x00, 0xa3, 0xb0, 0x5e,
+ 0x00, 0x65, 0xf2, 0x59,
+ 0x01, 0xa4, 0xca, 0x30,
+ 0x01, 0x55, 0x30, 0x7a,
+ 0x04, 0x65, 0xca, 0x00,
+ 0x80, 0xa3, 0x34, 0x7a,
+ 0x02, 0x65, 0xca, 0x00,
+ 0x01, 0x65, 0xf8, 0x31,
+ 0x80, 0x93, 0x26, 0x01,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x01, 0x8c, 0xc8, 0x30,
+ 0x00, 0x88, 0xc8, 0x18,
+ 0x02, 0x64, 0xc8, 0x88,
+ 0xff, 0x64, 0xd2, 0x7e,
+ 0xff, 0x8d, 0x4a, 0x6a,
+ 0xff, 0x8e, 0x4a, 0x6a,
+ 0x03, 0x8c, 0xd4, 0x98,
+ 0x00, 0x65, 0xd2, 0x56,
+ 0x01, 0x64, 0x70, 0x30,
+ 0xff, 0x64, 0xc8, 0x10,
+ 0x01, 0x64, 0xc8, 0x18,
+ 0x00, 0x8c, 0x18, 0x19,
+ 0xff, 0x8d, 0x1a, 0x21,
+ 0xff, 0x8e, 0x1c, 0x25,
+ 0x80, 0x3c, 0x5a, 0x6a,
+ 0x21, 0x6a, 0xce, 0x46,
+ 0xa8, 0x6a, 0x76, 0x00,
+ 0x79, 0x6a, 0x76, 0x00,
+ 0x40, 0x3f, 0x62, 0x6a,
+ 0x04, 0x3b, 0x76, 0x00,
+ 0x04, 0x6a, 0xd4, 0x81,
+ 0x20, 0x3c, 0x6a, 0x7a,
+ 0x51, 0x6a, 0xce, 0x5e,
+ 0x00, 0x65, 0x84, 0x42,
+ 0x20, 0x3c, 0x78, 0x00,
+ 0x00, 0xb3, 0xb0, 0x5e,
+ 0x07, 0xac, 0x10, 0x31,
+ 0x05, 0xb3, 0x46, 0x31,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xac, 0x6a, 0xe4, 0x5d,
+ 0xa3, 0x6a, 0xcc, 0x00,
+ 0xb3, 0x6a, 0xe8, 0x5d,
+ 0x00, 0x65, 0x3a, 0x5a,
+ 0xfd, 0xa4, 0x48, 0x09,
+ 0x01, 0x8c, 0xaa, 0x08,
+ 0x03, 0x8c, 0x10, 0x30,
+ 0x00, 0x65, 0xdc, 0x5d,
+ 0x01, 0xa4, 0x96, 0x7a,
+ 0x04, 0x3b, 0x76, 0x08,
+ 0x01, 0x3b, 0x26, 0x31,
+ 0x80, 0x02, 0x04, 0x00,
+ 0x10, 0x0c, 0x8c, 0x7a,
+ 0x03, 0x9e, 0x8e, 0x6a,
+ 0x7f, 0x02, 0x04, 0x08,
+ 0x91, 0x6a, 0xce, 0x5e,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0x01, 0xa4, 0xca, 0x30,
+ 0x80, 0xa3, 0x9c, 0x7a,
+ 0x02, 0x65, 0xca, 0x00,
+ 0x01, 0x55, 0xa0, 0x7a,
+ 0x04, 0x65, 0xca, 0x00,
+ 0x01, 0x65, 0xf8, 0x31,
+ 0x01, 0x3b, 0x26, 0x31,
+ 0x00, 0x65, 0x06, 0x5a,
+ 0x01, 0xfc, 0xae, 0x6a,
+ 0x80, 0x0b, 0xa4, 0x6a,
+ 0x10, 0x0c, 0xa4, 0x7a,
+ 0x20, 0x93, 0xa4, 0x6a,
+ 0x02, 0x93, 0x26, 0x01,
+ 0x02, 0xfc, 0xb8, 0x7a,
+ 0x40, 0x0d, 0xd2, 0x6a,
+ 0x01, 0xa4, 0x48, 0x01,
+ 0x00, 0x65, 0xd2, 0x42,
+ 0x40, 0x0d, 0xbe, 0x6a,
+ 0x00, 0x65, 0x06, 0x5a,
+ 0x00, 0x65, 0xb0, 0x42,
+ 0x80, 0xfc, 0xc8, 0x7a,
+ 0x80, 0xa4, 0xc8, 0x6a,
+ 0xff, 0xa5, 0x4a, 0x19,
+ 0xff, 0xa6, 0x4c, 0x21,
+ 0xff, 0xa7, 0x4e, 0x21,
+ 0xf8, 0xfc, 0x48, 0x09,
+ 0xff, 0x6a, 0xaa, 0x08,
+ 0x04, 0xfc, 0xd0, 0x7a,
+ 0x01, 0x55, 0xaa, 0x00,
+ 0xff, 0x6a, 0x46, 0x09,
+ 0x04, 0x3b, 0xea, 0x6a,
+ 0x02, 0x93, 0x26, 0x01,
+ 0x01, 0x94, 0xd4, 0x7a,
+ 0x01, 0x94, 0xd4, 0x7a,
+ 0x01, 0x94, 0xd4, 0x7a,
+ 0x01, 0x94, 0xd4, 0x7a,
+ 0x01, 0x94, 0xd4, 0x7a,
+ 0x01, 0xa4, 0xe8, 0x7a,
+ 0x01, 0xfc, 0xe2, 0x7a,
+ 0x01, 0x94, 0xea, 0x6a,
+ 0x00, 0x65, 0x84, 0x42,
+ 0x01, 0x94, 0xe8, 0x7a,
+ 0x10, 0x94, 0xea, 0x6a,
+ 0xd7, 0x93, 0x26, 0x09,
+ 0x28, 0x93, 0xee, 0x6a,
+ 0x01, 0x85, 0x0a, 0x01,
+ 0x02, 0xfc, 0xf6, 0x6a,
+ 0x01, 0x14, 0x46, 0x31,
+ 0xff, 0x6a, 0x10, 0x09,
+ 0xfe, 0x85, 0x0a, 0x09,
+ 0xff, 0x38, 0x04, 0x6b,
+ 0x80, 0xa3, 0x04, 0x7b,
+ 0x80, 0x0b, 0x02, 0x7b,
+ 0x04, 0x3b, 0x04, 0x7b,
+ 0xbf, 0x3b, 0x76, 0x08,
+ 0x01, 0x3b, 0x26, 0x31,
+ 0x00, 0x65, 0x06, 0x5a,
+ 0x01, 0x0b, 0x12, 0x6b,
+ 0x10, 0x0c, 0x06, 0x7b,
+ 0x04, 0x93, 0x10, 0x6b,
+ 0x01, 0x94, 0x0e, 0x7b,
+ 0x10, 0x94, 0x10, 0x6b,
+ 0xc7, 0x93, 0x26, 0x09,
+ 0x01, 0x99, 0xd4, 0x30,
+ 0x38, 0x93, 0x14, 0x6b,
+ 0xff, 0x08, 0x66, 0x6b,
+ 0xff, 0x09, 0x66, 0x6b,
+ 0xff, 0x0a, 0x66, 0x6b,
+ 0xff, 0x38, 0x30, 0x7b,
+ 0x04, 0x14, 0x10, 0x31,
+ 0x01, 0x38, 0x18, 0x31,
+ 0x02, 0x6a, 0x1a, 0x31,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x14, 0x6a, 0xea, 0x5d,
+ 0x00, 0x38, 0xd6, 0x5d,
+ 0xff, 0x6a, 0x70, 0x08,
+ 0x00, 0x65, 0x5c, 0x43,
+ 0x80, 0xa3, 0x36, 0x7b,
+ 0x01, 0xa4, 0x48, 0x01,
+ 0x00, 0x65, 0x66, 0x43,
+ 0x08, 0xeb, 0x3c, 0x7b,
+ 0x00, 0x65, 0x06, 0x5a,
+ 0x08, 0xeb, 0x38, 0x6b,
+ 0x07, 0xe9, 0x10, 0x31,
+ 0x01, 0xe9, 0xca, 0x30,
+ 0x01, 0x65, 0x46, 0x31,
+ 0x00, 0x6a, 0xb0, 0x5e,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xa4, 0x6a, 0xea, 0x5d,
+ 0x08, 0x6a, 0xd6, 0x5d,
+ 0x0d, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x9e, 0x5e,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x00, 0x65, 0x80, 0x5e,
+ 0x01, 0x99, 0x46, 0x31,
+ 0x00, 0xa3, 0xb0, 0x5e,
+ 0x01, 0x88, 0x10, 0x31,
+ 0x00, 0x65, 0x3a, 0x5a,
+ 0x00, 0x65, 0xf2, 0x59,
+ 0x03, 0x8c, 0x10, 0x30,
+ 0x00, 0x65, 0xdc, 0x5d,
+ 0x01, 0x8c, 0x64, 0x7b,
+ 0x01, 0x55, 0xaa, 0x10,
+ 0x80, 0x0b, 0x84, 0x6a,
+ 0x80, 0x0b, 0x6e, 0x6b,
+ 0x01, 0x0c, 0x68, 0x7b,
+ 0x10, 0x0c, 0x84, 0x7a,
+ 0x03, 0x9e, 0x84, 0x6a,
+ 0x00, 0x65, 0xfc, 0x59,
+ 0x00, 0x6a, 0xb0, 0x5e,
+ 0x01, 0xa4, 0x8e, 0x6b,
+ 0xff, 0x38, 0x84, 0x7b,
+ 0x01, 0x38, 0xc8, 0x30,
+ 0x00, 0x08, 0x40, 0x19,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x00, 0x09, 0x42, 0x21,
+ 0x00, 0x0a, 0x44, 0x21,
+ 0xff, 0x6a, 0x70, 0x08,
+ 0x00, 0x65, 0x86, 0x43,
+ 0x03, 0x08, 0x40, 0x31,
+ 0x03, 0x08, 0x40, 0x31,
+ 0x01, 0x08, 0x40, 0x31,
+ 0x01, 0x09, 0x42, 0x31,
+ 0x01, 0x0a, 0x44, 0x31,
+ 0xfd, 0xb4, 0x68, 0x09,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x04, 0x3c, 0xc4, 0x79,
+ 0xfb, 0x3c, 0x78, 0x08,
+ 0x04, 0x93, 0x1e, 0x79,
+ 0x01, 0x0c, 0x9a, 0x6b,
+ 0x01, 0x55, 0x1e, 0x79,
+ 0x80, 0x04, 0x1e, 0x79,
+ 0xe4, 0x6a, 0x64, 0x5d,
+ 0x23, 0x6a, 0x7a, 0x5d,
+ 0x01, 0x6a, 0x7a, 0x5d,
+ 0x00, 0x65, 0x1e, 0x41,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0x80, 0x3c, 0xae, 0x6b,
+ 0x21, 0x6a, 0xce, 0x46,
+ 0x01, 0xbc, 0x18, 0x31,
+ 0x02, 0x6a, 0x1a, 0x31,
+ 0x02, 0x6a, 0xf8, 0x01,
+ 0x01, 0xbc, 0x10, 0x30,
+ 0x02, 0x6a, 0x12, 0x30,
+ 0x01, 0xbc, 0x10, 0x30,
+ 0xff, 0x6a, 0x12, 0x08,
+ 0xff, 0x6a, 0x14, 0x08,
+ 0xf3, 0xbc, 0xd4, 0x18,
+ 0xa0, 0x6a, 0xd4, 0x53,
+ 0x04, 0xa0, 0x10, 0x31,
+ 0xac, 0x6a, 0x26, 0x01,
+ 0x04, 0xa0, 0x10, 0x31,
+ 0x03, 0x08, 0x18, 0x31,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xa0, 0x6a, 0xea, 0x5d,
+ 0x00, 0xbc, 0xd6, 0x5d,
+ 0x3d, 0x6a, 0x26, 0x01,
+ 0x00, 0x65, 0xec, 0x43,
+ 0xff, 0x6a, 0x10, 0x09,
+ 0xa4, 0x6a, 0x26, 0x01,
+ 0x0c, 0xa0, 0x32, 0x31,
+ 0x05, 0x6a, 0x26, 0x01,
+ 0x35, 0x6a, 0x26, 0x01,
+ 0x0c, 0xa0, 0x32, 0x31,
+ 0x36, 0x6a, 0x26, 0x01,
+ 0x02, 0x93, 0x26, 0x01,
+ 0x35, 0x6a, 0x26, 0x01,
+ 0x00, 0x65, 0x92, 0x5e,
+ 0x00, 0x65, 0x92, 0x5e,
+ 0x02, 0x93, 0x26, 0x01,
+ 0x04, 0x0b, 0xf0, 0x6b,
+ 0x10, 0x0c, 0xec, 0x7b,
+ 0x01, 0x03, 0xf0, 0x6b,
+ 0x20, 0x93, 0xec, 0x6b,
+ 0xc7, 0x93, 0x26, 0x09,
+ 0x38, 0x93, 0xf6, 0x6b,
+ 0x10, 0x01, 0x02, 0x00,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0x80, 0x3c, 0x00, 0x6c,
+ 0x21, 0x6a, 0xce, 0x46,
+ 0x01, 0x06, 0x50, 0x31,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0x10, 0x3f, 0x06, 0x00,
+ 0x10, 0x6a, 0x06, 0x00,
+ 0x01, 0x3a, 0xca, 0x30,
+ 0x80, 0x65, 0x24, 0x64,
+ 0x10, 0xb8, 0x48, 0x6c,
+ 0xc0, 0xba, 0xca, 0x00,
+ 0x40, 0xb8, 0x14, 0x6c,
+ 0xbf, 0x65, 0xca, 0x08,
+ 0x20, 0xb8, 0x28, 0x7c,
+ 0x01, 0x65, 0x0c, 0x30,
+ 0x00, 0x65, 0xce, 0x5d,
+ 0xa0, 0x3f, 0x30, 0x64,
+ 0x23, 0xb8, 0x0c, 0x08,
+ 0x00, 0x65, 0xce, 0x5d,
+ 0xa0, 0x3f, 0x30, 0x64,
+ 0x00, 0xbb, 0x28, 0x44,
+ 0xff, 0x65, 0x28, 0x64,
+ 0x00, 0x65, 0x48, 0x44,
+ 0x40, 0x6a, 0x18, 0x00,
+ 0x01, 0x65, 0x0c, 0x30,
+ 0x00, 0x65, 0xce, 0x5d,
+ 0xa0, 0x3f, 0x04, 0x74,
+ 0x40, 0x6a, 0x18, 0x00,
+ 0x01, 0x3a, 0xa6, 0x30,
+ 0x08, 0x6a, 0x74, 0x00,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0x64, 0x6a, 0x5e, 0x5d,
+ 0x80, 0x64, 0xce, 0x6c,
+ 0x04, 0x64, 0x94, 0x74,
+ 0x02, 0x64, 0xa2, 0x74,
+ 0x00, 0x6a, 0x64, 0x74,
+ 0x03, 0x64, 0xc0, 0x74,
+ 0x23, 0x64, 0x50, 0x74,
+ 0x08, 0x64, 0x60, 0x74,
+ 0x61, 0x6a, 0xce, 0x5e,
+ 0x00, 0x65, 0xce, 0x5d,
+ 0x08, 0x51, 0xc6, 0x71,
+ 0x00, 0x65, 0x48, 0x44,
+ 0x80, 0x04, 0x5e, 0x7c,
+ 0x51, 0x6a, 0x54, 0x5d,
+ 0x01, 0x51, 0x5e, 0x64,
+ 0x01, 0xa4, 0x5a, 0x7c,
+ 0x01, 0x55, 0x60, 0x7c,
+ 0x41, 0x6a, 0xce, 0x5e,
+ 0x00, 0x65, 0x60, 0x44,
+ 0x07, 0x6a, 0x4a, 0x5d,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0x10, 0xb8, 0x68, 0x7c,
+ 0xa1, 0x6a, 0xce, 0x5e,
+ 0x01, 0xb4, 0x6e, 0x6c,
+ 0x02, 0xb4, 0x70, 0x6c,
+ 0x01, 0xa4, 0x70, 0x7c,
+ 0xff, 0xa8, 0x80, 0x7c,
+ 0x04, 0xb4, 0x68, 0x01,
+ 0x01, 0x6a, 0x76, 0x00,
+ 0x00, 0xbb, 0x08, 0x5e,
+ 0xff, 0xa8, 0x80, 0x7c,
+ 0x71, 0x6a, 0xce, 0x5e,
+ 0x40, 0x51, 0x80, 0x64,
+ 0x00, 0x65, 0xa8, 0x5e,
+ 0x00, 0x65, 0xd6, 0x41,
+ 0x00, 0xbb, 0x84, 0x5c,
+ 0x00, 0x65, 0xd6, 0x41,
+ 0x00, 0x65, 0xa8, 0x5e,
+ 0x01, 0x65, 0xa2, 0x30,
+ 0x01, 0xf8, 0xc8, 0x30,
+ 0x01, 0x4e, 0xc8, 0x30,
+ 0x00, 0x6a, 0xac, 0xdd,
+ 0x00, 0x51, 0xbe, 0x5d,
+ 0x01, 0x4e, 0x9c, 0x18,
+ 0x02, 0x6a, 0x22, 0x05,
+ 0x04, 0xb8, 0x70, 0x01,
+ 0x00, 0x65, 0xca, 0x5e,
+ 0x20, 0xb8, 0xd6, 0x69,
+ 0x01, 0xbb, 0xa2, 0x30,
+ 0x01, 0xba, 0x7c, 0x30,
+ 0x00, 0xb9, 0xc4, 0x5c,
+ 0x00, 0x65, 0xd6, 0x41,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x20, 0x3c, 0xc4, 0x79,
+ 0x20, 0x3c, 0x60, 0x7c,
+ 0x01, 0xa4, 0xb0, 0x7c,
+ 0x01, 0xb4, 0x68, 0x01,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0x00, 0x65, 0x60, 0x44,
+ 0x04, 0x14, 0x58, 0x31,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x08, 0xa0, 0x60, 0x31,
+ 0xac, 0x6a, 0xcc, 0x00,
+ 0x14, 0x6a, 0xea, 0x5d,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0xa0, 0x6a, 0xe2, 0x5d,
+ 0x00, 0x65, 0xc4, 0x41,
+ 0xdf, 0x3c, 0x78, 0x08,
+ 0x00, 0x65, 0x60, 0x44,
+ 0x4c, 0x65, 0xcc, 0x28,
+ 0x01, 0x3e, 0x20, 0x31,
+ 0xd0, 0x66, 0xcc, 0x18,
+ 0x20, 0x66, 0xcc, 0x18,
+ 0x01, 0x51, 0xda, 0x34,
+ 0x4c, 0x3d, 0xca, 0x28,
+ 0x3f, 0x64, 0x7c, 0x08,
+ 0xd0, 0x65, 0xca, 0x18,
+ 0x01, 0x3e, 0x20, 0x31,
+ 0x30, 0x65, 0xd4, 0x18,
+ 0x00, 0x65, 0xdc, 0x4c,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x20, 0x65, 0xd4, 0x18,
+ 0x00, 0x65, 0xe4, 0x54,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x20, 0x65, 0xca, 0x18,
+ 0xe0, 0x65, 0xd4, 0x18,
+ 0x00, 0x65, 0xee, 0x4c,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xd0, 0x65, 0xd4, 0x18,
+ 0x00, 0x65, 0xf6, 0x54,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x01, 0x6c, 0xa2, 0x30,
+ 0xff, 0x51, 0x08, 0x75,
+ 0x00, 0x51, 0x84, 0x5d,
+ 0x01, 0x51, 0x20, 0x31,
+ 0x00, 0x65, 0x2a, 0x45,
+ 0x01, 0xba, 0xc8, 0x30,
+ 0x00, 0x3e, 0x2a, 0x75,
+ 0x00, 0x65, 0xa6, 0x5e,
+ 0x80, 0x3c, 0x78, 0x00,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x00, 0x65, 0xce, 0x5d,
+ 0x01, 0x3c, 0x78, 0x00,
+ 0xe0, 0x3f, 0x46, 0x65,
+ 0x02, 0x3c, 0x78, 0x00,
+ 0x20, 0x12, 0x46, 0x65,
+ 0x51, 0x6a, 0x54, 0x5d,
+ 0x00, 0x51, 0x84, 0x5d,
+ 0x51, 0x6a, 0x54, 0x5d,
+ 0x01, 0x51, 0x20, 0x31,
+ 0x04, 0x3c, 0x78, 0x00,
+ 0x01, 0xb9, 0xc8, 0x30,
+ 0x00, 0x3d, 0x44, 0x65,
+ 0x08, 0x3c, 0x78, 0x00,
+ 0x01, 0xba, 0xc8, 0x30,
+ 0x00, 0x3e, 0x44, 0x65,
+ 0x10, 0x3c, 0x78, 0x00,
+ 0x04, 0xb8, 0x44, 0x7d,
+ 0xfb, 0xb8, 0x70, 0x09,
+ 0x20, 0xb8, 0x3a, 0x6d,
+ 0x01, 0x90, 0xc8, 0x30,
+ 0xff, 0x6a, 0xa2, 0x00,
+ 0x00, 0x3d, 0xc4, 0x5c,
+ 0x01, 0x64, 0x20, 0x31,
+ 0x80, 0x6a, 0x78, 0x00,
+ 0x00, 0x65, 0xec, 0x58,
+ 0x10, 0xb8, 0x60, 0x7c,
+ 0xff, 0x6a, 0x4a, 0x5d,
+ 0x00, 0x65, 0x60, 0x44,
+ 0x00, 0x65, 0xa6, 0x5e,
+ 0x31, 0x6a, 0xce, 0x5e,
+ 0x00, 0x65, 0x60, 0x44,
+ 0x10, 0x3f, 0x06, 0x00,
+ 0x10, 0x6a, 0x06, 0x00,
+ 0x01, 0x65, 0x74, 0x34,
+ 0x81, 0x6a, 0xce, 0x5e,
+ 0x00, 0x65, 0x56, 0x45,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x01, 0x0c, 0x56, 0x7d,
+ 0x04, 0x0c, 0x50, 0x6d,
+ 0xe0, 0x03, 0x7e, 0x08,
+ 0xe0, 0x3f, 0xc4, 0x61,
+ 0x01, 0x65, 0xcc, 0x30,
+ 0x01, 0x12, 0xda, 0x34,
+ 0x01, 0x06, 0xd4, 0x34,
+ 0x01, 0x03, 0x64, 0x6d,
+ 0x40, 0x03, 0xcc, 0x08,
+ 0x01, 0x65, 0x06, 0x30,
+ 0x40, 0x65, 0xc8, 0x08,
+ 0x00, 0x66, 0x72, 0x75,
+ 0x40, 0x65, 0x72, 0x7d,
+ 0x00, 0x65, 0x72, 0x5d,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x08, 0x01, 0x02, 0x00,
+ 0x02, 0x0b, 0x7c, 0x7d,
+ 0x01, 0x65, 0x0c, 0x30,
+ 0x02, 0x0b, 0x80, 0x7d,
+ 0xf7, 0x01, 0x02, 0x0c,
+ 0x01, 0x65, 0xc8, 0x30,
+ 0xff, 0x41, 0xa4, 0x75,
+ 0x01, 0x41, 0x20, 0x31,
+ 0xff, 0x6a, 0xa4, 0x00,
+ 0x00, 0x65, 0x94, 0x45,
+ 0xff, 0xbf, 0xa4, 0x75,
+ 0x01, 0x90, 0xa4, 0x30,
+ 0x01, 0xbf, 0x20, 0x31,
+ 0x00, 0xbb, 0x8e, 0x65,
+ 0xff, 0x52, 0xa2, 0x75,
+ 0x01, 0xbf, 0xcc, 0x30,
+ 0x01, 0x90, 0xca, 0x30,
+ 0x01, 0x52, 0x20, 0x31,
+ 0x01, 0x66, 0x7e, 0x31,
+ 0x01, 0x65, 0x20, 0x35,
+ 0x01, 0xbf, 0x82, 0x34,
+ 0x01, 0x64, 0xa2, 0x30,
+ 0x00, 0x6a, 0xb6, 0x5e,
+ 0x0d, 0x6a, 0x76, 0x00,
+ 0x00, 0x51, 0x08, 0x46,
+ 0x01, 0x65, 0xa4, 0x30,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x48, 0x6a, 0xfc, 0x5d,
+ 0x01, 0x6a, 0xd0, 0x01,
+ 0x01, 0x6a, 0xdc, 0x05,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x48, 0x6a, 0xfc, 0x5d,
+ 0x01, 0x6a, 0xd6, 0x5d,
+ 0x01, 0x6a, 0x26, 0x05,
+ 0x01, 0x65, 0xd8, 0x31,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0xc2, 0x7d,
+ 0xff, 0x6a, 0xdc, 0x0d,
+ 0x01, 0x65, 0x32, 0x31,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x9e, 0x46,
+ 0x81, 0x6a, 0xce, 0x5e,
+ 0x01, 0x0c, 0xce, 0x7d,
+ 0x04, 0x0c, 0xcc, 0x6d,
+ 0xe0, 0x03, 0x06, 0x08,
+ 0xe0, 0x03, 0x7e, 0x0c,
+ 0x01, 0x65, 0x18, 0x31,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x0d,
+ 0x01, 0x8c, 0x10, 0x30,
+ 0x01, 0x8d, 0x12, 0x30,
+ 0x01, 0x8e, 0x14, 0x34,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x34,
+ 0x3d, 0x64, 0xa4, 0x28,
+ 0x55, 0x64, 0xc8, 0x28,
+ 0x00, 0x65, 0xfc, 0x45,
+ 0x2e, 0x64, 0xa4, 0x28,
+ 0x66, 0x64, 0xc8, 0x28,
+ 0x00, 0x6c, 0xda, 0x18,
+ 0x01, 0x52, 0xc8, 0x30,
+ 0x00, 0x6c, 0xda, 0x20,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x00, 0x6c, 0xda, 0x20,
+ 0x00, 0x6c, 0xda, 0x24,
+ 0x01, 0x65, 0xc8, 0x30,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x44, 0x6a, 0xf8, 0x5d,
+ 0x01, 0x90, 0xe2, 0x31,
+ 0x04, 0x3b, 0x1c, 0x7e,
+ 0x30, 0x6a, 0xd0, 0x01,
+ 0x20, 0x6a, 0xd0, 0x01,
+ 0x1d, 0x6a, 0xdc, 0x01,
+ 0xdc, 0xee, 0x18, 0x66,
+ 0x00, 0x65, 0x34, 0x46,
+ 0x20, 0x6a, 0xd0, 0x01,
+ 0x01, 0x6a, 0xdc, 0x01,
+ 0x20, 0xa0, 0xd8, 0x31,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0x24, 0x7e,
+ 0x11, 0x6a, 0xdc, 0x01,
+ 0x50, 0xee, 0x28, 0x66,
+ 0x20, 0x6a, 0xd0, 0x01,
+ 0x09, 0x6a, 0xdc, 0x01,
+ 0x88, 0xee, 0x2e, 0x66,
+ 0x19, 0x6a, 0xdc, 0x01,
+ 0xd8, 0xee, 0x32, 0x66,
+ 0xff, 0x6a, 0xdc, 0x09,
+ 0x18, 0xee, 0x36, 0x6e,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x44, 0x6a, 0xf8, 0x5d,
+ 0x20, 0x6a, 0xd6, 0x5d,
+ 0x01, 0x3b, 0x26, 0x31,
+ 0x04, 0x3b, 0x50, 0x6e,
+ 0xa0, 0x6a, 0xca, 0x00,
+ 0x20, 0x65, 0xc8, 0x18,
+ 0x00, 0x65, 0x8e, 0x5e,
+ 0x00, 0x65, 0x48, 0x66,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x9e, 0x46,
+ 0xa0, 0x6a, 0xcc, 0x00,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x20, 0x94, 0x54, 0x6e,
+ 0x10, 0x94, 0x56, 0x6e,
+ 0x08, 0x94, 0x70, 0x6e,
+ 0x08, 0x94, 0x70, 0x6e,
+ 0x08, 0x94, 0x70, 0x6e,
+ 0xff, 0x8c, 0xc8, 0x10,
+ 0xc1, 0x64, 0xc8, 0x18,
+ 0xf8, 0x64, 0xc8, 0x08,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x00, 0x66, 0x64, 0x66,
+ 0xc0, 0x66, 0xa0, 0x76,
+ 0x60, 0x66, 0xc8, 0x18,
+ 0x3d, 0x64, 0xc8, 0x28,
+ 0x00, 0x65, 0x54, 0x46,
+ 0xf7, 0x93, 0x26, 0x09,
+ 0x08, 0x93, 0x72, 0x6e,
+ 0x00, 0x62, 0xc4, 0x18,
+ 0x00, 0x65, 0x9e, 0x5e,
+ 0x00, 0x65, 0x7e, 0x5e,
+ 0x00, 0x65, 0x7e, 0x5e,
+ 0x00, 0x65, 0x7e, 0x5e,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x34,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x35,
+ 0x08, 0x94, 0x9e, 0x7e,
+ 0xf7, 0x93, 0x26, 0x09,
+ 0x08, 0x93, 0xa2, 0x6e,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x04, 0xb8, 0xca, 0x6e,
+ 0x01, 0x42, 0x7e, 0x31,
+ 0xff, 0x6a, 0x76, 0x01,
+ 0x01, 0x90, 0x84, 0x34,
+ 0xff, 0x6a, 0x76, 0x05,
+ 0x01, 0x85, 0x0a, 0x01,
+ 0x7f, 0x65, 0x10, 0x09,
+ 0xfe, 0x85, 0x0a, 0x0d,
+ 0xff, 0x42, 0xc6, 0x66,
+ 0xff, 0x41, 0xbe, 0x66,
+ 0xd1, 0x6a, 0xce, 0x5e,
+ 0xff, 0x6a, 0xca, 0x04,
+ 0x01, 0x41, 0x20, 0x31,
+ 0x01, 0xbf, 0x82, 0x30,
+ 0x01, 0x6a, 0x76, 0x00,
+ 0x00, 0xbb, 0x08, 0x46,
+ 0x01, 0x42, 0x20, 0x31,
+ 0x01, 0xbf, 0x84, 0x34,
+ 0x01, 0x41, 0x7e, 0x31,
+ 0x01, 0x90, 0x82, 0x34,
+ 0x01, 0x65, 0x22, 0x31,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xff, 0x6a, 0xd4, 0x0c
+};
+
+static int aic_patch23_func(struct ahc_softc *ahc);
+
+static int
+aic_patch23_func(struct ahc_softc *ahc)
+{
+ return ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0);
+}
+
+static int aic_patch22_func(struct ahc_softc *ahc);
+
+static int
+aic_patch22_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_CMD_CHAN) == 0);
+}
+
+static int aic_patch21_func(struct ahc_softc *ahc);
+
+static int
+aic_patch21_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_QUEUE_REGS) == 0);
+}
+
+static int aic_patch20_func(struct ahc_softc *ahc);
+
+static int
+aic_patch20_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_WIDE) != 0);
+}
+
+static int aic_patch19_func(struct ahc_softc *ahc);
+
+static int
+aic_patch19_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_SCB_BTT) != 0);
+}
+
+static int aic_patch18_func(struct ahc_softc *ahc);
+
+static int
+aic_patch18_func(struct ahc_softc *ahc)
+{
+ return ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0);
+}
+
+static int aic_patch17_func(struct ahc_softc *ahc);
+
+static int
+aic_patch17_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0);
+}
+
+static int aic_patch16_func(struct ahc_softc *ahc);
+
+static int
+aic_patch16_func(struct ahc_softc *ahc)
+{
+ return ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0);
+}
+
+static int aic_patch15_func(struct ahc_softc *ahc);
+
+static int
+aic_patch15_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_ULTRA2) == 0);
+}
+
+static int aic_patch14_func(struct ahc_softc *ahc);
+
+static int
+aic_patch14_func(struct ahc_softc *ahc)
+{
+ return ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0);
+}
+
+static int aic_patch13_func(struct ahc_softc *ahc);
+
+static int
+aic_patch13_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_39BIT_ADDRESSING) != 0);
+}
+
+static int aic_patch12_func(struct ahc_softc *ahc);
+
+static int
+aic_patch12_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_HS_MAILBOX) != 0);
+}
+
+static int aic_patch11_func(struct ahc_softc *ahc);
+
+static int
+aic_patch11_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_ULTRA) != 0);
+}
+
+static int aic_patch10_func(struct ahc_softc *ahc);
+
+static int
+aic_patch10_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_MULTI_TID) != 0);
+}
+
+static int aic_patch9_func(struct ahc_softc *ahc);
+
+static int
+aic_patch9_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_CMD_CHAN) != 0);
+}
+
+static int aic_patch8_func(struct ahc_softc *ahc);
+
+static int
+aic_patch8_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_INITIATORROLE) != 0);
+}
+
+static int aic_patch7_func(struct ahc_softc *ahc);
+
+static int
+aic_patch7_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_TARGETROLE) != 0);
+}
+
+static int aic_patch6_func(struct ahc_softc *ahc);
+
+static int
+aic_patch6_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_DT) == 0);
+}
+
+static int aic_patch5_func(struct ahc_softc *ahc);
+
+static int
+aic_patch5_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0);
+}
+
+static int aic_patch4_func(struct ahc_softc *ahc);
+
+static int
+aic_patch4_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_PAGESCBS) != 0);
+}
+
+static int aic_patch3_func(struct ahc_softc *ahc);
+
+static int
+aic_patch3_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_QUEUE_REGS) != 0);
+}
+
+static int aic_patch2_func(struct ahc_softc *ahc);
+
+static int
+aic_patch2_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_TWIN) != 0);
+}
+
+static int aic_patch1_func(struct ahc_softc *ahc);
+
+static int
+aic_patch1_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_ULTRA2) != 0);
+}
+
+static int aic_patch0_func(struct ahc_softc *ahc);
+
+static int
+aic_patch0_func(struct ahc_softc *ahc)
+{
+ return (0);
+}
+
+typedef int patch_func_t (struct ahc_softc *ahc);
+static struct patch {
+ patch_func_t *patch_func;
+ uint32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
+} patches[] = {
+ { aic_patch1_func, 4, 1, 1 },
+ { aic_patch2_func, 6, 2, 1 },
+ { aic_patch2_func, 9, 1, 1 },
+ { aic_patch3_func, 11, 1, 2 },
+ { aic_patch0_func, 12, 2, 1 },
+ { aic_patch4_func, 15, 1, 2 },
+ { aic_patch0_func, 16, 1, 1 },
+ { aic_patch5_func, 22, 2, 1 },
+ { aic_patch3_func, 27, 1, 2 },
+ { aic_patch0_func, 28, 1, 1 },
+ { aic_patch6_func, 34, 1, 1 },
+ { aic_patch7_func, 37, 54, 19 },
+ { aic_patch8_func, 37, 1, 1 },
+ { aic_patch9_func, 42, 3, 2 },
+ { aic_patch0_func, 45, 3, 1 },
+ { aic_patch10_func, 49, 1, 2 },
+ { aic_patch0_func, 50, 2, 3 },
+ { aic_patch1_func, 50, 1, 2 },
+ { aic_patch0_func, 51, 1, 1 },
+ { aic_patch2_func, 53, 2, 1 },
+ { aic_patch9_func, 55, 1, 2 },
+ { aic_patch0_func, 56, 1, 1 },
+ { aic_patch9_func, 60, 1, 2 },
+ { aic_patch0_func, 61, 1, 1 },
+ { aic_patch9_func, 71, 1, 2 },
+ { aic_patch0_func, 72, 1, 1 },
+ { aic_patch9_func, 75, 1, 2 },
+ { aic_patch0_func, 76, 1, 1 },
+ { aic_patch9_func, 79, 1, 2 },
+ { aic_patch0_func, 80, 1, 1 },
+ { aic_patch8_func, 91, 9, 4 },
+ { aic_patch1_func, 93, 1, 2 },
+ { aic_patch0_func, 94, 1, 1 },
+ { aic_patch2_func, 96, 2, 1 },
+ { aic_patch2_func, 105, 4, 1 },
+ { aic_patch1_func, 109, 1, 2 },
+ { aic_patch0_func, 110, 2, 3 },
+ { aic_patch2_func, 110, 1, 2 },
+ { aic_patch0_func, 111, 1, 1 },
+ { aic_patch7_func, 112, 4, 2 },
+ { aic_patch0_func, 116, 1, 1 },
+ { aic_patch11_func, 118, 2, 1 },
+ { aic_patch1_func, 120, 1, 2 },
+ { aic_patch0_func, 121, 1, 1 },
+ { aic_patch7_func, 122, 4, 1 },
+ { aic_patch7_func, 132, 91, 11 },
+ { aic_patch4_func, 151, 1, 1 },
+ { aic_patch1_func, 165, 1, 1 },
+ { aic_patch12_func, 170, 1, 2 },
+ { aic_patch0_func, 171, 1, 1 },
+ { aic_patch9_func, 182, 1, 2 },
+ { aic_patch0_func, 183, 1, 1 },
+ { aic_patch9_func, 192, 1, 2 },
+ { aic_patch0_func, 193, 1, 1 },
+ { aic_patch9_func, 209, 6, 2 },
+ { aic_patch0_func, 215, 6, 1 },
+ { aic_patch8_func, 223, 19, 2 },
+ { aic_patch1_func, 237, 1, 1 },
+ { aic_patch1_func, 244, 1, 2 },
+ { aic_patch0_func, 245, 2, 2 },
+ { aic_patch11_func, 246, 1, 1 },
+ { aic_patch9_func, 254, 31, 3 },
+ { aic_patch1_func, 270, 14, 2 },
+ { aic_patch13_func, 275, 1, 1 },
+ { aic_patch14_func, 285, 14, 1 },
+ { aic_patch1_func, 301, 1, 2 },
+ { aic_patch0_func, 302, 1, 1 },
+ { aic_patch9_func, 305, 1, 1 },
+ { aic_patch13_func, 310, 1, 1 },
+ { aic_patch9_func, 311, 2, 2 },
+ { aic_patch0_func, 313, 4, 1 },
+ { aic_patch14_func, 317, 1, 1 },
+ { aic_patch15_func, 320, 2, 3 },
+ { aic_patch9_func, 320, 1, 2 },
+ { aic_patch0_func, 321, 1, 1 },
+ { aic_patch6_func, 326, 1, 2 },
+ { aic_patch0_func, 327, 1, 1 },
+ { aic_patch1_func, 331, 50, 11 },
+ { aic_patch6_func, 340, 2, 4 },
+ { aic_patch7_func, 340, 1, 1 },
+ { aic_patch8_func, 341, 1, 1 },
+ { aic_patch0_func, 342, 1, 1 },
+ { aic_patch16_func, 343, 1, 1 },
+ { aic_patch6_func, 362, 6, 3 },
+ { aic_patch16_func, 362, 5, 1 },
+ { aic_patch0_func, 368, 5, 1 },
+ { aic_patch13_func, 376, 5, 1 },
+ { aic_patch0_func, 381, 54, 17 },
+ { aic_patch14_func, 381, 1, 1 },
+ { aic_patch7_func, 383, 2, 2 },
+ { aic_patch17_func, 384, 1, 1 },
+ { aic_patch9_func, 387, 1, 1 },
+ { aic_patch18_func, 394, 1, 1 },
+ { aic_patch14_func, 399, 9, 3 },
+ { aic_patch9_func, 400, 3, 2 },
+ { aic_patch0_func, 403, 3, 1 },
+ { aic_patch9_func, 411, 6, 2 },
+ { aic_patch0_func, 417, 9, 2 },
+ { aic_patch13_func, 417, 1, 1 },
+ { aic_patch13_func, 426, 2, 1 },
+ { aic_patch14_func, 428, 1, 1 },
+ { aic_patch9_func, 430, 1, 2 },
+ { aic_patch0_func, 431, 1, 1 },
+ { aic_patch7_func, 434, 1, 1 },
+ { aic_patch7_func, 435, 1, 1 },
+ { aic_patch8_func, 436, 3, 3 },
+ { aic_patch6_func, 437, 1, 2 },
+ { aic_patch0_func, 438, 1, 1 },
+ { aic_patch9_func, 439, 1, 1 },
+ { aic_patch15_func, 440, 1, 2 },
+ { aic_patch13_func, 440, 1, 1 },
+ { aic_patch14_func, 442, 9, 4 },
+ { aic_patch9_func, 442, 1, 1 },
+ { aic_patch9_func, 449, 2, 1 },
+ { aic_patch0_func, 451, 4, 3 },
+ { aic_patch9_func, 451, 1, 2 },
+ { aic_patch0_func, 452, 3, 1 },
+ { aic_patch1_func, 456, 2, 1 },
+ { aic_patch7_func, 458, 10, 2 },
+ { aic_patch0_func, 468, 1, 1 },
+ { aic_patch8_func, 469, 109, 23 },
+ { aic_patch1_func, 471, 3, 2 },
+ { aic_patch0_func, 474, 5, 3 },
+ { aic_patch9_func, 474, 2, 2 },
+ { aic_patch0_func, 476, 3, 1 },
+ { aic_patch1_func, 481, 2, 2 },
+ { aic_patch0_func, 483, 6, 3 },
+ { aic_patch9_func, 483, 2, 2 },
+ { aic_patch0_func, 485, 3, 1 },
+ { aic_patch1_func, 491, 2, 2 },
+ { aic_patch0_func, 493, 9, 7 },
+ { aic_patch9_func, 493, 5, 6 },
+ { aic_patch19_func, 493, 1, 2 },
+ { aic_patch0_func, 494, 1, 1 },
+ { aic_patch19_func, 496, 1, 2 },
+ { aic_patch0_func, 497, 1, 1 },
+ { aic_patch0_func, 498, 4, 1 },
+ { aic_patch6_func, 502, 3, 2 },
+ { aic_patch0_func, 505, 1, 1 },
+ { aic_patch1_func, 508, 1, 1 },
+ { aic_patch6_func, 514, 1, 2 },
+ { aic_patch0_func, 515, 1, 1 },
+ { aic_patch20_func, 552, 7, 1 },
+ { aic_patch3_func, 580, 1, 2 },
+ { aic_patch0_func, 581, 1, 1 },
+ { aic_patch21_func, 584, 1, 1 },
+ { aic_patch8_func, 586, 104, 33 },
+ { aic_patch4_func, 587, 1, 1 },
+ { aic_patch1_func, 593, 2, 2 },
+ { aic_patch0_func, 595, 1, 1 },
+ { aic_patch1_func, 598, 1, 2 },
+ { aic_patch0_func, 599, 1, 1 },
+ { aic_patch9_func, 600, 3, 3 },
+ { aic_patch15_func, 601, 1, 1 },
+ { aic_patch0_func, 603, 4, 1 },
+ { aic_patch19_func, 611, 2, 2 },
+ { aic_patch0_func, 613, 1, 1 },
+ { aic_patch19_func, 617, 10, 3 },
+ { aic_patch5_func, 619, 8, 1 },
+ { aic_patch0_func, 627, 9, 2 },
+ { aic_patch5_func, 628, 8, 1 },
+ { aic_patch4_func, 638, 1, 2 },
+ { aic_patch0_func, 639, 1, 1 },
+ { aic_patch19_func, 640, 1, 2 },
+ { aic_patch0_func, 641, 3, 2 },
+ { aic_patch4_func, 643, 1, 1 },
+ { aic_patch5_func, 644, 1, 1 },
+ { aic_patch5_func, 647, 1, 1 },
+ { aic_patch5_func, 649, 1, 1 },
+ { aic_patch4_func, 651, 2, 2 },
+ { aic_patch0_func, 653, 2, 1 },
+ { aic_patch5_func, 655, 1, 1 },
+ { aic_patch5_func, 658, 1, 1 },
+ { aic_patch5_func, 661, 1, 1 },
+ { aic_patch19_func, 665, 1, 1 },
+ { aic_patch19_func, 668, 1, 1 },
+ { aic_patch4_func, 674, 1, 1 },
+ { aic_patch6_func, 677, 1, 2 },
+ { aic_patch0_func, 678, 1, 1 },
+ { aic_patch7_func, 690, 16, 1 },
+ { aic_patch4_func, 706, 20, 1 },
+ { aic_patch9_func, 727, 4, 2 },
+ { aic_patch0_func, 731, 4, 1 },
+ { aic_patch9_func, 735, 4, 2 },
+ { aic_patch0_func, 739, 3, 1 },
+ { aic_patch6_func, 745, 1, 1 },
+ { aic_patch22_func, 747, 14, 1 },
+ { aic_patch7_func, 761, 3, 1 },
+ { aic_patch9_func, 773, 24, 8 },
+ { aic_patch19_func, 777, 1, 2 },
+ { aic_patch0_func, 778, 1, 1 },
+ { aic_patch15_func, 783, 4, 2 },
+ { aic_patch0_func, 787, 7, 3 },
+ { aic_patch23_func, 787, 5, 2 },
+ { aic_patch0_func, 792, 2, 1 },
+ { aic_patch0_func, 797, 42, 3 },
+ { aic_patch18_func, 809, 18, 2 },
+ { aic_patch0_func, 827, 1, 1 },
+ { aic_patch4_func, 851, 1, 1 },
+ { aic_patch4_func, 852, 3, 2 },
+ { aic_patch0_func, 855, 1, 1 },
+ { aic_patch13_func, 856, 3, 1 },
+ { aic_patch4_func, 859, 12, 1 }
+};
+static struct cs {
+ u_int16_t begin;
+ u_int16_t end;
+} critical_sections[] = {
+ { 11, 18 },
+ { 21, 30 },
+ { 706, 722 },
+ { 852, 855 },
+ { 859, 865 },
+ { 867, 869 },
+ { 869, 871 }
+};
+static const int num_critical_sections = sizeof(critical_sections)
+ / sizeof(*critical_sections);
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/Makefile b/xen/drivers/scsi/aic7xxx/aicasm/Makefile
new file mode 100644
index 0000000000..16394e1f8f
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/Makefile
@@ -0,0 +1,65 @@
+PROG= aicasm
+
+.SUFFIXES= .l .y .c .h
+
+CSRCS= aicasm.c aicasm_symbol.c
+YSRCS= aicasm_gram.y aicasm_macro_gram.y
+LSRCS= aicasm_scan.l aicasm_macro_scan.l
+
+GENHDRS= aicdb.h $(YSRCS:.y=.h)
+GENSRCS= $(YSRCS:.y=.c) $(LSRCS:.l=.c)
+
+SRCS= ${CSRCS} ${GENSRCS}
+CLEANFILES= ${GENSRCS} ${GENHDRS} $(YSRCS:.y=.output)
+# Override default kernel CFLAGS. This is a userland app.
+AICASM_CFLAGS:= -I/usr/include -I. -ldb
+YFLAGS= -d
+
+NOMAN= noman
+
+ifneq ($(HOSTCC),)
+AICASM_CC= $(HOSTCC)
+else
+AICASM_CC= $(CC)
+endif
+
+ifdef DEBUG
+CFLAGS+= -DDEBUG -g
+YFLAGS+= -t -v
+LFLAGS= -d
+endif
+
+$(PROG): ${GENHDRS} $(SRCS)
+ $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG)
+
+aicdb.h:
+ @if [ -e "/usr/include/db3/db_185.h" ]; then \
+ echo "#include <db3/db_185.h>" > aicdb.h; \
+ elif [ -e "/usr/include/db2/db_185.h" ]; then \
+ echo "#include <db2/db_185.h>" > aicdb.h; \
+ elif [ -e "/usr/include/db/db_185.h" ]; then \
+ echo "#include <db/db_185.h>" > aicdb.h; \
+ elif [ -e "/usr/include/db_185.h" ]; then \
+ echo "#include <db_185.h>" > aicdb.h; \
+ else \
+ echo "*** Install db development libraries"; \
+ fi
+
+clean:
+ rm -f $(CLEANFILES) $(PROG)
+
+aicasm_gram.c aicasm_gram.h: aicasm_gram.y
+ $(YACC) $(YFLAGS) -b $(<:.y=) $<
+ mv $(<:.y=).tab.c $(<:.y=.c)
+ mv $(<:.y=).tab.h $(<:.y=.h)
+
+aicasm_macro_gram.c aicasm_macro_gram.h: aicasm_macro_gram.y
+ $(YACC) $(YFLAGS) -b $(<:.y=) -p mm $<
+ mv $(<:.y=).tab.c $(<:.y=.c)
+ mv $(<:.y=).tab.h $(<:.y=.h)
+
+aicasm_scan.c: aicasm_scan.l
+ $(LEX) $(LFLAGS) -o$@ $<
+
+aicasm_macro_scan.c: aicasm_macro_scan.l
+ $(LEX) $(LFLAGS) -Pmm -o$@ $<
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm.c b/xen/drivers/scsi/aic7xxx/aicasm/aicasm.c
new file mode 100644
index 0000000000..bc93c3d6ec
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/aicasm.c
@@ -0,0 +1,809 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler
+ *
+ * Copyright (c) 1997, 1998, 2000, 2001 Justin T. Gibbs.
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.c#16 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm.c,v 1.28.2.5 2002/04/29 19:36:36 gibbs Exp $
+ */
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#if linux
+#include <endian.h>
+#else
+#include <machine/endian.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+typedef struct patch {
+ STAILQ_ENTRY(patch) links;
+ int patch_func;
+ u_int begin;
+ u_int skip_instr;
+ u_int skip_patch;
+} patch_t;
+
+STAILQ_HEAD(patch_list, patch) patches;
+
+static void usage(void);
+static void back_patch(void);
+static void output_code(void);
+static void output_listing(char *ifilename);
+static void dump_scope(scope_t *scope);
+static void emit_patch(scope_t *scope, int patch);
+static int check_patch(patch_t **start_patch, int start_instr,
+ int *skip_addr, int *func_vals);
+
+struct path_list search_path;
+int includes_search_curdir;
+char *appname;
+FILE *ofile;
+char *ofilename;
+char *regfilename;
+FILE *regfile;
+char *listfilename;
+FILE *listfile;
+int src_mode;
+int dst_mode;
+
+static STAILQ_HEAD(,instruction) seq_program;
+struct cs_tailq cs_tailq;
+struct scope_list scope_stack;
+symlist_t patch_functions;
+
+#if DEBUG
+extern int yy_flex_debug;
+extern int mm_flex_debug;
+extern int yydebug;
+extern int mmdebug;
+#endif
+extern FILE *yyin;
+extern int yyparse(void);
+
+int main(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ int ch;
+ int retval;
+ char *inputfilename;
+ scope_t *sentinal;
+
+ STAILQ_INIT(&patches);
+ SLIST_INIT(&search_path);
+ STAILQ_INIT(&seq_program);
+ TAILQ_INIT(&cs_tailq);
+ SLIST_INIT(&scope_stack);
+
+ /* Set Sentinal scope node */
+ sentinal = scope_alloc();
+ sentinal->type = SCOPE_ROOT;
+
+ includes_search_curdir = 1;
+ appname = *argv;
+ regfile = NULL;
+ listfile = NULL;
+#if DEBUG
+ yy_flex_debug = 0;
+ mm_flex_debug = 0;
+ yydebug = 0;
+ mmdebug = 0;
+#endif
+ while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != -1) {
+ switch(ch) {
+ case 'd':
+#if DEBUG
+ if (strcmp(optarg, "s") == 0) {
+ yy_flex_debug = 1;
+ mm_flex_debug = 1;
+ } else if (strcmp(optarg, "p") == 0) {
+ yydebug = 1;
+ mmdebug = 1;
+ } else {
+ fprintf(stderr, "%s: -d Requires either an "
+ "'s' or 'p' argument\n", appname);
+ usage();
+ }
+#else
+ stop("-d: Assembler not built with debugging "
+ "information", EX_SOFTWARE);
+#endif
+ break;
+ case 'l':
+ /* Create a program listing */
+ if ((listfile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ listfilename = optarg;
+ break;
+ case 'n':
+ /* Don't complain about the -nostdinc directrive */
+ if (strcmp(optarg, "ostdinc")) {
+ fprintf(stderr, "%s: Unknown option -%c%s\n",
+ appname, ch, optarg);
+ usage();
+ /* NOTREACHED */
+ }
+ break;
+ case 'o':
+ if ((ofile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ ofilename = optarg;
+ break;
+ case 'r':
+ if ((regfile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ regfilename = optarg;
+ break;
+ case 'I':
+ {
+ path_entry_t include_dir;
+
+ if (strcmp(optarg, "-") == 0) {
+ if (includes_search_curdir == 0) {
+ fprintf(stderr, "%s: Warning - '-I-' "
+ "specified multiple "
+ "times\n", appname);
+ }
+ includes_search_curdir = 0;
+ for (include_dir = SLIST_FIRST(&search_path);
+ include_dir != NULL;
+ include_dir = SLIST_NEXT(include_dir,
+ links))
+ /*
+ * All entries before a '-I-' only
+ * apply to includes specified with
+ * quotes instead of "<>".
+ */
+ include_dir->quoted_includes_only = 1;
+ } else {
+ include_dir =
+ (path_entry_t)malloc(sizeof(*include_dir));
+ if (include_dir == NULL) {
+ perror(optarg);
+ stop(NULL, EX_OSERR);
+ }
+ include_dir->directory = strdup(optarg);
+ if (include_dir->directory == NULL) {
+ perror(optarg);
+ stop(NULL, EX_OSERR);
+ }
+ include_dir->quoted_includes_only = 0;
+ SLIST_INSERT_HEAD(&search_path, include_dir,
+ links);
+ }
+ break;
+ }
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ fprintf(stderr, "%s: No input file specifiled\n", appname);
+ usage();
+ /* NOTREACHED */
+ }
+
+ symtable_open();
+ inputfilename = *argv;
+ include_file(*argv, SOURCE_FILE);
+ retval = yyparse();
+ if (retval == 0) {
+ if (SLIST_FIRST(&scope_stack) == NULL
+ || SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) {
+ stop("Unterminated conditional expression", EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ /* Process outmost scope */
+ process_scope(SLIST_FIRST(&scope_stack));
+ /*
+ * Decend the tree of scopes and insert/emit
+ * patches as appropriate. We perform a depth first
+ * tranversal, recursively handling each scope.
+ */
+ /* start at the root scope */
+ dump_scope(SLIST_FIRST(&scope_stack));
+
+ /* Patch up forward jump addresses */
+ back_patch();
+
+ if (ofile != NULL)
+ output_code();
+ if (regfile != NULL) {
+ symtable_dump(regfile);
+ }
+ if (listfile != NULL)
+ output_listing(inputfilename);
+ }
+
+ stop(NULL, 0);
+ /* NOTREACHED */
+ return (0);
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr,
+"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]
+ [-r register_output_file] [-l program_list_file]
+ input_file\n",
+ appname);
+ exit(EX_USAGE);
+}
+
+static void
+back_patch()
+{
+ struct instruction *cur_instr;
+
+ for (cur_instr = STAILQ_FIRST(&seq_program);
+ cur_instr != NULL;
+ cur_instr = STAILQ_NEXT(cur_instr, links)) {
+ if (cur_instr->patch_label != NULL) {
+ struct ins_format3 *f3_instr;
+ u_int address;
+
+ if (cur_instr->patch_label->type != LABEL) {
+ char buf[255];
+
+ snprintf(buf, sizeof(buf),
+ "Undefined label %s",
+ cur_instr->patch_label->name);
+ stop(buf, EX_DATAERR);
+ /* NOTREACHED */
+ }
+ f3_instr = &cur_instr->format.format3;
+ address = f3_instr->address;
+ address += cur_instr->patch_label->info.linfo->address;
+ f3_instr->address = address;
+ }
+ }
+}
+
+static void
+output_code()
+{
+ struct instruction *cur_instr;
+ patch_t *cur_patch;
+ critical_section_t *cs;
+ symbol_node_t *cur_node;
+ int instrcount;
+
+ instrcount = 0;
+ fprintf(ofile,
+"/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+%s */\n", versions);
+
+ fprintf(ofile, "static uint8_t seqprog[] = {\n");
+ for (cur_instr = STAILQ_FIRST(&seq_program);
+ cur_instr != NULL;
+ cur_instr = STAILQ_NEXT(cur_instr, links)) {
+
+ fprintf(ofile, "%s\t0x%02x, 0x%02x, 0x%02x, 0x%02x",
+ cur_instr == STAILQ_FIRST(&seq_program) ? "" : ",\n",
+#if BYTE_ORDER == LITTLE_ENDIAN
+ cur_instr->format.bytes[0],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[3]);
+#else
+ cur_instr->format.bytes[3],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[0]);
+#endif
+ instrcount++;
+ }
+ fprintf(ofile, "\n};\n\n");
+
+ if (patch_arg_list == NULL)
+ stop("Patch argument list not defined",
+ EX_DATAERR);
+
+ /*
+ * Output patch information. Patch functions first.
+ */
+ for (cur_node = SLIST_FIRST(&patch_functions);
+ cur_node != NULL;
+ cur_node = SLIST_NEXT(cur_node,links)) {
+ fprintf(ofile,
+"static int aic_patch%d_func(%s);
+
+static int
+aic_patch%d_func(%s)
+{
+ return (%s);
+}\n\n",
+ cur_node->symbol->info.condinfo->func_num,
+ patch_arg_list,
+ cur_node->symbol->info.condinfo->func_num,
+ patch_arg_list,
+ cur_node->symbol->name);
+ }
+
+ fprintf(ofile,
+"typedef int patch_func_t (%s);
+static struct patch {
+ patch_func_t *patch_func;
+ uint32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
+} patches[] = {\n", patch_arg_list);
+
+ for (cur_patch = STAILQ_FIRST(&patches);
+ cur_patch != NULL;
+ cur_patch = STAILQ_NEXT(cur_patch,links)) {
+ fprintf(ofile, "%s\t{ aic_patch%d_func, %d, %d, %d }",
+ cur_patch == STAILQ_FIRST(&patches) ? "" : ",\n",
+ cur_patch->patch_func, cur_patch->begin,
+ cur_patch->skip_instr, cur_patch->skip_patch);
+ }
+
+ fprintf(ofile, "\n};\n");
+
+ fprintf(ofile,
+"static struct cs {
+ u_int16_t begin;
+ u_int16_t end;
+} critical_sections[] = {\n");
+
+ for (cs = TAILQ_FIRST(&cs_tailq);
+ cs != NULL;
+ cs = TAILQ_NEXT(cs, links)) {
+ fprintf(ofile, "%s\t{ %d, %d }",
+ cs == TAILQ_FIRST(&cs_tailq) ? "" : ",\n",
+ cs->begin_addr, cs->end_addr);
+ }
+
+ fprintf(ofile, "\n};\n");
+
+ fprintf(ofile,
+"static const int num_critical_sections = sizeof(critical_sections)
+ / sizeof(*critical_sections);\n");
+
+ fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
+}
+
+static void
+dump_scope(scope_t *scope)
+{
+ scope_t *cur_scope;
+
+ /*
+ * Emit the first patch for this scope
+ */
+ emit_patch(scope, 0);
+
+ /*
+ * Dump each scope within this one.
+ */
+ cur_scope = TAILQ_FIRST(&scope->inner_scope);
+
+ while (cur_scope != NULL) {
+
+ dump_scope(cur_scope);
+
+ cur_scope = TAILQ_NEXT(cur_scope, scope_links);
+ }
+
+ /*
+ * Emit the second, closing, patch for this scope
+ */
+ emit_patch(scope, 1);
+}
+
+void
+emit_patch(scope_t *scope, int patch)
+{
+ patch_info_t *pinfo;
+ patch_t *new_patch;
+
+ pinfo = &scope->patches[patch];
+
+ if (pinfo->skip_instr == 0)
+ /* No-Op patch */
+ return;
+
+ new_patch = (patch_t *)malloc(sizeof(*new_patch));
+
+ if (new_patch == NULL)
+ stop("Could not malloc patch structure", EX_OSERR);
+
+ memset(new_patch, 0, sizeof(*new_patch));
+
+ if (patch == 0) {
+ new_patch->patch_func = scope->func_num;
+ new_patch->begin = scope->begin_addr;
+ } else {
+ new_patch->patch_func = 0;
+ new_patch->begin = scope->end_addr;
+ }
+ new_patch->skip_instr = pinfo->skip_instr;
+ new_patch->skip_patch = pinfo->skip_patch;
+ STAILQ_INSERT_TAIL(&patches, new_patch, links);
+}
+
+void
+output_listing(char *ifilename)
+{
+ char buf[1024];
+ FILE *ifile;
+ struct instruction *cur_instr;
+ patch_t *cur_patch;
+ symbol_node_t *cur_func;
+ int *func_values;
+ int instrcount;
+ int instrptr;
+ int line;
+ int func_count;
+ int skip_addr;
+
+ instrcount = 0;
+ instrptr = 0;
+ line = 1;
+ skip_addr = 0;
+ if ((ifile = fopen(ifilename, "r")) == NULL) {
+ perror(ifilename);
+ stop(NULL, EX_DATAERR);
+ }
+
+ /*
+ * Determine which options to apply to this listing.
+ */
+ for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions);
+ cur_func != NULL;
+ cur_func = SLIST_NEXT(cur_func, links))
+ func_count++;
+
+ func_values = NULL;
+ if (func_count != 0) {
+ func_values = (int *)malloc(func_count * sizeof(int));
+
+ if (func_values == NULL)
+ stop("Could not malloc", EX_OSERR);
+
+ func_values[0] = 0; /* FALSE func */
+ func_count--;
+
+ /*
+ * Ask the user to fill in the return values for
+ * the rest of the functions.
+ */
+
+
+ for (cur_func = SLIST_FIRST(&patch_functions);
+ cur_func != NULL && SLIST_NEXT(cur_func, links) != NULL;
+ cur_func = SLIST_NEXT(cur_func, links), func_count--) {
+ int input;
+
+ fprintf(stdout, "\n(%s)\n", cur_func->symbol->name);
+ fprintf(stdout,
+ "Enter the return value for "
+ "this expression[T/F]:");
+
+ while (1) {
+
+ input = getchar();
+ input = toupper(input);
+
+ if (input == 'T') {
+ func_values[func_count] = 1;
+ break;
+ } else if (input == 'F') {
+ func_values[func_count] = 0;
+ break;
+ }
+ }
+ if (isatty(fileno(stdin)) == 0)
+ putchar(input);
+ }
+ fprintf(stdout, "\nThanks!\n");
+ }
+
+ /* Now output the listing */
+ cur_patch = STAILQ_FIRST(&patches);
+ for (cur_instr = STAILQ_FIRST(&seq_program);
+ cur_instr != NULL;
+ cur_instr = STAILQ_NEXT(cur_instr, links), instrcount++) {
+
+ if (check_patch(&cur_patch, instrcount,
+ &skip_addr, func_values) == 0) {
+ /* Don't count this instruction as it is in a patch
+ * that was removed.
+ */
+ continue;
+ }
+
+ while (line < cur_instr->srcline) {
+ fgets(buf, sizeof(buf), ifile);
+ fprintf(listfile, "\t\t%s", buf);
+ line++;
+ }
+ fprintf(listfile, "%03x %02x%02x%02x%02x", instrptr,
+#if BYTE_ORDER == LITTLE_ENDIAN
+ cur_instr->format.bytes[0],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[3]);
+#else
+ cur_instr->format.bytes[3],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[0]);
+#endif
+ fgets(buf, sizeof(buf), ifile);
+ fprintf(listfile, "\t%s", buf);
+ line++;
+ instrptr++;
+ }
+ /* Dump the remainder of the file */
+ while(fgets(buf, sizeof(buf), ifile) != NULL)
+ fprintf(listfile, "\t\t%s", buf);
+
+ fclose(ifile);
+}
+
+static int
+check_patch(patch_t **start_patch, int start_instr,
+ int *skip_addr, int *func_vals)
+{
+ patch_t *cur_patch;
+
+ cur_patch = *start_patch;
+
+ while (cur_patch != NULL && start_instr == cur_patch->begin) {
+ if (func_vals[cur_patch->patch_func] == 0) {
+ int skip;
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ for (skip = cur_patch->skip_patch;
+ skip > 0 && cur_patch != NULL;
+ skip--)
+ cur_patch = STAILQ_NEXT(cur_patch, links);
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
+ */
+ cur_patch = STAILQ_NEXT(cur_patch, links);
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Print out error information if appropriate, and clean up before
+ * terminating the program.
+ */
+void
+stop(const char *string, int err_code)
+{
+ if (string != NULL) {
+ fprintf(stderr, "%s: ", appname);
+ if (yyfilename != NULL) {
+ fprintf(stderr, "Stopped at file %s, line %d - ",
+ yyfilename, yylineno);
+ }
+ fprintf(stderr, "%s\n", string);
+ }
+
+ if (ofile != NULL) {
+ fclose(ofile);
+ if (err_code != 0) {
+ fprintf(stderr, "%s: Removing %s due to error\n",
+ appname, ofilename);
+ unlink(ofilename);
+ }
+ }
+
+ if (regfile != NULL) {
+ fclose(regfile);
+ if (err_code != 0) {
+ fprintf(stderr, "%s: Removing %s due to error\n",
+ appname, regfilename);
+ unlink(regfilename);
+ }
+ }
+
+ if (listfile != NULL) {
+ fclose(listfile);
+ if (err_code != 0) {
+ fprintf(stderr, "%s: Removing %s due to error\n",
+ appname, listfilename);
+ unlink(listfilename);
+ }
+ }
+
+ symlist_free(&patch_functions);
+ symtable_close();
+
+ exit(err_code);
+}
+
+struct instruction *
+seq_alloc()
+{
+ struct instruction *new_instr;
+
+ new_instr = (struct instruction *)malloc(sizeof(struct instruction));
+ if (new_instr == NULL)
+ stop("Unable to malloc instruction object", EX_SOFTWARE);
+ memset(new_instr, 0, sizeof(*new_instr));
+ STAILQ_INSERT_TAIL(&seq_program, new_instr, links);
+ new_instr->srcline = yylineno;
+ return new_instr;
+}
+
+critical_section_t *
+cs_alloc()
+{
+ critical_section_t *new_cs;
+
+ new_cs= (critical_section_t *)malloc(sizeof(critical_section_t));
+ if (new_cs == NULL)
+ stop("Unable to malloc critical_section object", EX_SOFTWARE);
+ memset(new_cs, 0, sizeof(*new_cs));
+
+ TAILQ_INSERT_TAIL(&cs_tailq, new_cs, links);
+ return new_cs;
+}
+
+scope_t *
+scope_alloc()
+{
+ scope_t *new_scope;
+
+ new_scope = (scope_t *)malloc(sizeof(scope_t));
+ if (new_scope == NULL)
+ stop("Unable to malloc scope object", EX_SOFTWARE);
+ memset(new_scope, 0, sizeof(*new_scope));
+ TAILQ_INIT(&new_scope->inner_scope);
+
+ if (SLIST_FIRST(&scope_stack) != NULL) {
+ TAILQ_INSERT_TAIL(&SLIST_FIRST(&scope_stack)->inner_scope,
+ new_scope, scope_links);
+ }
+ /* This patch is now the current scope */
+ SLIST_INSERT_HEAD(&scope_stack, new_scope, scope_stack_links);
+ return new_scope;
+}
+
+void
+process_scope(scope_t *scope)
+{
+ /*
+ * We are "leaving" this scope. We should now have
+ * enough information to process the lists of scopes
+ * we encapsulate.
+ */
+ scope_t *cur_scope;
+ u_int skip_patch_count;
+ u_int skip_instr_count;
+
+ cur_scope = TAILQ_LAST(&scope->inner_scope, scope_tailq);
+ skip_patch_count = 0;
+ skip_instr_count = 0;
+ while (cur_scope != NULL) {
+ u_int patch0_patch_skip;
+
+ patch0_patch_skip = 0;
+ switch (cur_scope->type) {
+ case SCOPE_IF:
+ case SCOPE_ELSE_IF:
+ if (skip_instr_count != 0) {
+ /* Create a tail patch */
+ patch0_patch_skip++;
+ cur_scope->patches[1].skip_patch =
+ skip_patch_count + 1;
+ cur_scope->patches[1].skip_instr =
+ skip_instr_count;
+ }
+
+ /* Count Head patch */
+ patch0_patch_skip++;
+
+ /* Count any patches contained in our inner scope */
+ patch0_patch_skip += cur_scope->inner_scope_patches;
+
+ cur_scope->patches[0].skip_patch = patch0_patch_skip;
+ cur_scope->patches[0].skip_instr =
+ cur_scope->end_addr - cur_scope->begin_addr;
+
+ skip_instr_count += cur_scope->patches[0].skip_instr;
+
+ skip_patch_count += patch0_patch_skip;
+ if (cur_scope->type == SCOPE_IF) {
+ scope->inner_scope_patches += skip_patch_count;
+ skip_patch_count = 0;
+ skip_instr_count = 0;
+ }
+ break;
+ case SCOPE_ELSE:
+ /* Count any patches contained in our innter scope */
+ skip_patch_count += cur_scope->inner_scope_patches;
+
+ skip_instr_count += cur_scope->end_addr
+ - cur_scope->begin_addr;
+ break;
+ case SCOPE_ROOT:
+ stop("Unexpected scope type encountered", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ cur_scope = TAILQ_PREV(cur_scope, scope_tailq, scope_links);
+ }
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm.h b/xen/drivers/scsi/aic7xxx/aicasm/aicasm.h
new file mode 100644
index 0000000000..d18314a2b3
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/aicasm.h
@@ -0,0 +1,93 @@
+/*
+ * Assembler for the sequencer program downloaded to Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.h#10 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm.h,v 1.11.2.4 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef struct path_entry {
+ char *directory;
+ int quoted_includes_only;
+ SLIST_ENTRY(path_entry) links;
+} *path_entry_t;
+
+typedef enum {
+ QUOTED_INCLUDE,
+ BRACKETED_INCLUDE,
+ SOURCE_FILE
+} include_type;
+
+SLIST_HEAD(path_list, path_entry);
+
+extern struct path_list search_path;
+extern struct cs_tailq cs_tailq;
+extern struct scope_list scope_stack;
+extern struct symlist patch_functions;
+extern int includes_search_curdir; /* False if we've seen -I- */
+extern char *appname;
+extern int yylineno;
+extern char *yyfilename;
+extern char *patch_arg_list;
+extern char *versions;
+extern int src_mode;
+extern int dst_mode;
+struct symbol;
+
+void stop(const char *errstring, int err_code);
+void include_file(char *file_name, include_type type);
+void expand_macro(struct symbol *macro_symbol);
+struct instruction *seq_alloc(void);
+struct critical_section *cs_alloc(void);
+struct scope *scope_alloc(void);
+void process_scope(struct scope *);
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
new file mode 100644
index 0000000000..6d9213e3ae
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
@@ -0,0 +1,1858 @@
+%{
+/*
+ * Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_gram.y#15 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_gram.y,v 1.11.2.5 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+int yylineno;
+char *yyfilename;
+char *patch_arg_list;
+char *versions;
+static char errbuf[255];
+static char regex_pattern[255];
+static symbol_t *cur_symbol;
+static symbol_t *scb_or_sram_symbol;
+static symtype cur_symtype;
+static symbol_ref_t accumulator;
+static symbol_ref_t mode_ptr;
+static symbol_ref_t allones;
+static symbol_ref_t allzeros;
+static symbol_ref_t none;
+static symbol_ref_t sindex;
+static int instruction_ptr;
+static int num_srams;
+static int sram_or_scb_offset;
+static int download_constant_count;
+static int in_critical_section;
+
+static void process_bitmask(int mask_type, symbol_t *sym, int mask);
+static void initialize_symbol(symbol_t *symbol);
+static void add_macro_arg(const char *argtext, int position);
+static void add_macro_body(const char *bodytext);
+static void process_register(symbol_t **p_symbol);
+static void format_1_instr(int opcode, symbol_ref_t *dest,
+ expression_t *immed, symbol_ref_t *src, int ret);
+static void format_2_instr(int opcode, symbol_ref_t *dest,
+ expression_t *places, symbol_ref_t *src, int ret);
+static void format_3_instr(int opcode, symbol_ref_t *src,
+ expression_t *immed, symbol_ref_t *address);
+static void test_readable_symbol(symbol_t *symbol);
+static void test_writable_symbol(symbol_t *symbol);
+static void type_check(symbol_t *symbol, expression_t *expression, int and_op);
+static void make_expression(expression_t *immed, int value);
+static void add_conditional(symbol_t *symbol);
+static void add_version(const char *verstring);
+static int is_download_const(expression_t *immed);
+
+#define SRAM_SYMNAME "SRAM_BASE"
+#define SCB_SYMNAME "SCB_BASE"
+%}
+
+%union {
+ u_int value;
+ char *str;
+ symbol_t *sym;
+ symbol_ref_t sym_ref;
+ expression_t expression;
+}
+
+%token T_REGISTER
+
+%token <value> T_CONST
+
+%token T_EXPORT
+
+%token T_DOWNLOAD
+
+%token T_SCB
+
+%token T_SRAM
+
+%token T_ALIAS
+
+%token T_SIZE
+
+%token T_EXPR_LSHIFT
+
+%token T_EXPR_RSHIFT
+
+%token <value> T_ADDRESS
+
+%token T_ACCESS_MODE
+
+%token T_MODES
+
+%token T_DEFINE
+
+%token T_SET_SRC_MODE
+
+%token T_SET_DST_MODE
+
+%token <value> T_MODE
+
+%token T_BEGIN_CS
+
+%token T_END_CS
+
+%token T_BIT
+
+%token T_MASK
+
+%token <value> T_NUMBER
+
+%token <str> T_PATH T_STRING T_ARG T_MACROBODY
+
+%token <sym> T_CEXPR
+
+%token T_EOF T_INCLUDE T_VERSION T_PATCH_ARG_LIST
+
+%token <value> T_SHR T_SHL T_ROR T_ROL
+
+%token <value> T_MVI T_MOV T_CLR T_BMOV
+
+%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL
+
+%token <value> T_ADD T_ADC
+
+%token <value> T_INC T_DEC
+
+%token <value> T_STC T_CLC
+
+%token <value> T_CMP T_NOT T_XOR
+
+%token <value> T_TEST T_AND
+
+%token <value> T_OR
+
+%token T_RET
+
+%token T_NOP
+
+%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX T_MODE_PTR
+
+%token T_A
+
+%token <sym> T_SYMBOL
+
+%token T_NL
+
+%token T_IF T_ELSE T_ELSE_IF T_ENDIF
+
+%type <sym_ref> reg_symbol address destination source opt_source
+
+%type <expression> expression immediate immediate_or_a
+
+%type <value> export ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne
+
+%type <value> numerical_value mode_value mode_list macro_arglist
+
+%left '|'
+%left '&'
+%left T_EXPR_LSHIFT T_EXPR_RSHIFT
+%left '+' '-'
+%left '*' '/'
+%right '~'
+%nonassoc UMINUS
+%%
+
+program:
+ include
+| program include
+| patch_arg_list
+| program patch_arg_list
+| version
+| program version
+| register
+| program register
+| constant
+| program constant
+| macrodefn
+| program macrodefn
+| scratch_ram
+| program scratch_ram
+| scb
+| program scb
+| label
+| program label
+| set_src_mode
+| program set_src_mode
+| set_dst_mode
+| program set_dst_mode
+| critical_section_start
+| program critical_section_start
+| critical_section_end
+| program critical_section_end
+| conditional
+| program conditional
+| code
+| program code
+;
+
+include:
+ T_INCLUDE '<' T_PATH '>'
+ {
+ include_file($3, BRACKETED_INCLUDE);
+ }
+| T_INCLUDE '"' T_PATH '"'
+ {
+ include_file($3, QUOTED_INCLUDE);
+ }
+;
+
+patch_arg_list:
+ T_PATCH_ARG_LIST '=' T_STRING
+ {
+ if (patch_arg_list != NULL)
+ stop("Patch argument list multiply defined",
+ EX_DATAERR);
+ patch_arg_list = strdup($3);
+ if (patch_arg_list == NULL)
+ stop("Unable to record patch arg list", EX_SOFTWARE);
+ }
+;
+
+version:
+ T_VERSION '=' T_STRING
+ { add_version($3); }
+;
+
+register:
+ T_REGISTER { cur_symtype = REGISTER; } reg_definition
+;
+
+reg_definition:
+ T_SYMBOL '{'
+ {
+ if ($1->type != UNINITIALIZED) {
+ stop("Register multiply defined", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cur_symbol = $1;
+ cur_symbol->type = cur_symtype;
+ initialize_symbol(cur_symbol);
+ }
+ reg_attribute_list
+ '}'
+ {
+ /*
+ * Default to allowing everything in for registers
+ * with no bit or mask definitions.
+ */
+ if (cur_symbol->info.rinfo->valid_bitmask == 0)
+ cur_symbol->info.rinfo->valid_bitmask = 0xFF;
+
+ if (cur_symbol->info.rinfo->size == 0)
+ cur_symbol->info.rinfo->size = 1;
+
+ /*
+ * This might be useful for registers too.
+ */
+ if (cur_symbol->type != REGISTER) {
+ if (cur_symbol->info.rinfo->address == 0)
+ cur_symbol->info.rinfo->address =
+ sram_or_scb_offset;
+ sram_or_scb_offset +=
+ cur_symbol->info.rinfo->size;
+ }
+ cur_symbol = NULL;
+ }
+;
+
+reg_attribute_list:
+ reg_attribute
+| reg_attribute_list reg_attribute
+;
+
+reg_attribute:
+ reg_address
+| size
+| access_mode
+| modes
+| bit_defn
+| mask_defn
+| alias
+| accumulator
+| mode_pointer
+| allones
+| allzeros
+| none
+| sindex
+;
+
+reg_address:
+ T_ADDRESS T_NUMBER
+ {
+ cur_symbol->info.rinfo->address = $2;
+ }
+;
+
+size:
+ T_SIZE T_NUMBER
+ {
+ cur_symbol->info.rinfo->size = $2;
+ if (scb_or_sram_symbol != NULL) {
+ u_int max_addr;
+ u_int sym_max_addr;
+
+ max_addr = scb_or_sram_symbol->info.rinfo->address
+ + scb_or_sram_symbol->info.rinfo->size;
+ sym_max_addr = cur_symbol->info.rinfo->address
+ + cur_symbol->info.rinfo->size;
+
+ if (sym_max_addr > max_addr)
+ stop("SCB or SRAM space exhausted", EX_DATAERR);
+ }
+ }
+;
+
+access_mode:
+ T_ACCESS_MODE T_MODE
+ {
+ cur_symbol->info.rinfo->mode = $2;
+ }
+;
+
+modes:
+ T_MODES mode_list
+ {
+ cur_symbol->info.rinfo->modes = $2;
+ }
+;
+
+mode_list:
+ mode_value
+ {
+ $$ = $1;
+ }
+| mode_list ',' mode_value
+ {
+ $$ = $1 | $3;
+ }
+;
+
+mode_value:
+ T_NUMBER
+ {
+ if ($1 > 4) {
+ stop("Valid register modes range between 0 and 4.",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ $$ = (0x1 << $1);
+ }
+| T_SYMBOL
+ {
+ symbol_t *symbol;
+
+ symbol = $1;
+ if (symbol->type != CONST) {
+ stop("Only \"const\" symbols allowed in "
+ "mode definitions.", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if (symbol->info.cinfo->value > 4) {
+ stop("Valid register modes range between 0 and 4.",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$ = (0x1 << symbol->info.cinfo->value);
+ }
+;
+
+bit_defn:
+ T_BIT T_SYMBOL T_NUMBER
+ {
+ process_bitmask(BIT, $2, $3);
+ }
+;
+
+mask_defn:
+ T_MASK T_SYMBOL expression
+ {
+ process_bitmask(MASK, $2, $3.value);
+ }
+;
+
+alias:
+ T_ALIAS T_SYMBOL
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of register alias",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = ALIAS;
+ initialize_symbol($2);
+ $2->info.ainfo->parent = cur_symbol;
+ }
+;
+
+accumulator:
+ T_ACCUM
+ {
+ if (accumulator.symbol != NULL) {
+ stop("Only one accumulator definition allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ accumulator.symbol = cur_symbol;
+ }
+;
+
+mode_pointer:
+ T_MODE_PTR
+ {
+ if (mode_ptr.symbol != NULL) {
+ stop("Only one mode pointer definition allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ mode_ptr.symbol = cur_symbol;
+ }
+;
+
+allones:
+ T_ALLONES
+ {
+ if (allones.symbol != NULL) {
+ stop("Only one definition of allones allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ allones.symbol = cur_symbol;
+ }
+;
+
+allzeros:
+ T_ALLZEROS
+ {
+ if (allzeros.symbol != NULL) {
+ stop("Only one definition of allzeros allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ allzeros.symbol = cur_symbol;
+ }
+;
+
+none:
+ T_NONE
+ {
+ if (none.symbol != NULL) {
+ stop("Only one definition of none allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ none.symbol = cur_symbol;
+ }
+;
+
+sindex:
+ T_SINDEX
+ {
+ if (sindex.symbol != NULL) {
+ stop("Only one definition of sindex allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ sindex.symbol = cur_symbol;
+ }
+;
+
+expression:
+ expression '|' expression
+ {
+ $$.value = $1.value | $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression '&' expression
+ {
+ $$.value = $1.value & $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression '+' expression
+ {
+ $$.value = $1.value + $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression '-' expression
+ {
+ $$.value = $1.value - $3.value;
+ symlist_merge(&($$.referenced_syms),
+ &($1.referenced_syms),
+ &($3.referenced_syms));
+ }
+| expression '*' expression
+ {
+ $$.value = $1.value * $3.value;
+ symlist_merge(&($$.referenced_syms),
+ &($1.referenced_syms),
+ &($3.referenced_syms));
+ }
+| expression '/' expression
+ {
+ $$.value = $1.value / $3.value;
+ symlist_merge(&($$.referenced_syms),
+ &($1.referenced_syms),
+ &($3.referenced_syms));
+ }
+| expression T_EXPR_LSHIFT expression
+ {
+ $$.value = $1.value << $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression T_EXPR_RSHIFT expression
+ {
+ $$.value = $1.value >> $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| '(' expression ')'
+ {
+ $$ = $2;
+ }
+| '~' expression
+ {
+ $$ = $2;
+ $$.value = (~$$.value) & 0xFF;
+ }
+| '-' expression %prec UMINUS
+ {
+ $$ = $2;
+ $$.value = -$$.value;
+ }
+| T_NUMBER
+ {
+ $$.value = $1;
+ SLIST_INIT(&$$.referenced_syms);
+ }
+| T_SYMBOL
+ {
+ symbol_t *symbol;
+
+ symbol = $1;
+ switch (symbol->type) {
+ case ALIAS:
+ symbol = $1->info.ainfo->parent;
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ $$.value = symbol->info.rinfo->address;
+ break;
+ case MASK:
+ case BIT:
+ $$.value = symbol->info.minfo->mask;
+ break;
+ case DOWNLOAD_CONST:
+ case CONST:
+ $$.value = symbol->info.cinfo->value;
+ break;
+ case UNINITIALIZED:
+ default:
+ {
+ snprintf(errbuf, sizeof(errbuf),
+ "Undefined symbol %s referenced",
+ symbol->name);
+ stop(errbuf, EX_DATAERR);
+ /* NOTREACHED */
+ break;
+ }
+ }
+ SLIST_INIT(&$$.referenced_syms);
+ symlist_add(&$$.referenced_syms, symbol, SYMLIST_INSERT_HEAD);
+ }
+;
+
+constant:
+ T_CONST T_SYMBOL numerical_value
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of symbol as a constant",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = CONST;
+ initialize_symbol($2);
+ $2->info.cinfo->value = $3;
+ }
+| T_CONST T_SYMBOL T_DOWNLOAD
+ {
+ if ($1) {
+ stop("Invalid downloaded constant declaration",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of symbol as a downloaded constant",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = DOWNLOAD_CONST;
+ initialize_symbol($2);
+ $2->info.cinfo->value = download_constant_count++;
+ }
+;
+
+macrodefn_prologue:
+ T_DEFINE T_SYMBOL
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of symbol as a macro",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cur_symbol = $2;
+ cur_symbol->type = MACRO;
+ initialize_symbol(cur_symbol);
+ }
+;
+
+macrodefn:
+ macrodefn_prologue T_MACROBODY
+ {
+ add_macro_body($2);
+ }
+| macrodefn_prologue '(' macro_arglist ')' T_MACROBODY
+ {
+ add_macro_body($5);
+ cur_symbol->info.macroinfo->narg = $3;
+ }
+;
+
+macro_arglist:
+ {
+ /* Macros can take no arguments */
+ $$ = 0;
+ }
+| T_ARG
+ {
+ $$ = 1;
+ add_macro_arg($1, 0);
+ }
+| macro_arglist ',' T_ARG
+ {
+ if ($1 == 0) {
+ stop("Comma without preceeding argument in arg list",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$ = $1 + 1;
+ add_macro_arg($3, $1);
+ }
+;
+
+numerical_value:
+ T_NUMBER
+ {
+ $$ = $1;
+ }
+| '-' T_NUMBER
+ {
+ $$ = -$2;
+ }
+;
+
+scratch_ram:
+ T_SRAM '{'
+ {
+ snprintf(errbuf, sizeof(errbuf), "%s%d", SRAM_SYMNAME,
+ num_srams);
+ cur_symbol = symtable_get(SRAM_SYMNAME);
+ cur_symtype = SRAMLOC;
+ cur_symbol->type = SRAMLOC;
+ initialize_symbol(cur_symbol);
+ }
+ reg_address
+ {
+ sram_or_scb_offset = cur_symbol->info.rinfo->address;
+ }
+ size
+ {
+ scb_or_sram_symbol = cur_symbol;
+ }
+ scb_or_sram_attributes
+ '}'
+ {
+ cur_symbol = NULL;
+ scb_or_sram_symbol = NULL;
+ }
+;
+
+scb:
+ T_SCB '{'
+ {
+ cur_symbol = symtable_get(SCB_SYMNAME);
+ cur_symtype = SCBLOC;
+ if (cur_symbol->type != UNINITIALIZED) {
+ stop("Only one SRAM definition allowed",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ cur_symbol->type = SCBLOC;
+ initialize_symbol(cur_symbol);
+ /* 64 bytes of SCB space */
+ cur_symbol->info.rinfo->size = 64;
+ }
+ reg_address
+ {
+ sram_or_scb_offset = cur_symbol->info.rinfo->address;
+ }
+ size
+ {
+ scb_or_sram_symbol = cur_symbol;
+ }
+ scb_or_sram_attributes
+ '}'
+ {
+ cur_symbol = NULL;
+ scb_or_sram_symbol = NULL;
+ }
+;
+
+scb_or_sram_attributes:
+ /* NULL definition is okay */
+| modes
+| scb_or_sram_reg_list
+| modes scb_or_sram_reg_list
+;
+
+scb_or_sram_reg_list:
+ reg_definition
+| scb_or_sram_reg_list reg_definition
+;
+
+reg_symbol:
+ T_SYMBOL
+ {
+ process_register(&$1);
+ $$.symbol = $1;
+ $$.offset = 0;
+ }
+| T_SYMBOL '[' T_SYMBOL ']'
+ {
+ process_register(&$1);
+ if ($3->type != CONST) {
+ stop("register offset must be a constant", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if (($3->info.cinfo->value + 1) > $1->info.rinfo->size) {
+ stop("Accessing offset beyond range of register",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$.symbol = $1;
+ $$.offset = $3->info.cinfo->value;
+ }
+| T_SYMBOL '[' T_NUMBER ']'
+ {
+ process_register(&$1);
+ if (($3 + 1) > $1->info.rinfo->size) {
+ stop("Accessing offset beyond range of register",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$.symbol = $1;
+ $$.offset = $3;
+ }
+| T_A
+ {
+ if (accumulator.symbol == NULL) {
+ stop("No accumulator has been defined", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$.symbol = accumulator.symbol;
+ $$.offset = 0;
+ }
+;
+
+destination:
+ reg_symbol
+ {
+ test_writable_symbol($1.symbol);
+ $$ = $1;
+ }
+;
+
+immediate:
+ expression
+ { $$ = $1; }
+;
+
+immediate_or_a:
+ expression
+ {
+ if ($1.value == 0 && is_download_const(&$1) == 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "\nExpression evaluates to 0 and thus "
+ "references the accumulator.\n "
+ "If this is the desired effect, use 'A' "
+ "instead.\n");
+ stop(errbuf, EX_DATAERR);
+ }
+ $$ = $1;
+ }
+| T_A
+ {
+ SLIST_INIT(&$$.referenced_syms);
+ $$.value = 0;
+ }
+;
+
+source:
+ reg_symbol
+ {
+ test_readable_symbol($1.symbol);
+ $$ = $1;
+ }
+;
+
+opt_source:
+ {
+ $$.symbol = NULL;
+ $$.offset = 0;
+ }
+| ',' source
+ { $$ = $2; }
+;
+
+ret:
+ { $$ = 0; }
+| T_RET
+ { $$ = 1; }
+;
+
+set_src_mode:
+ T_SET_SRC_MODE T_NUMBER ';'
+ {
+ src_mode = $2;
+ }
+;
+
+set_dst_mode:
+ T_SET_DST_MODE T_NUMBER ';'
+ {
+ dst_mode = $2;
+ }
+;
+
+critical_section_start:
+ T_BEGIN_CS ';'
+ {
+ critical_section_t *cs;
+
+ if (in_critical_section != FALSE) {
+ stop("Critical Section within Critical Section",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cs = cs_alloc();
+ cs->begin_addr = instruction_ptr;
+ in_critical_section = TRUE;
+ }
+
+critical_section_end:
+ T_END_CS ';'
+ {
+ critical_section_t *cs;
+
+ if (in_critical_section == FALSE) {
+ stop("Unballanced 'end_cs'", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cs = TAILQ_LAST(&cs_tailq, cs_tailq);
+ cs->end_addr = instruction_ptr;
+ in_critical_section = FALSE;
+ }
+
+export:
+ { $$ = 0; }
+| T_EXPORT
+ { $$ = 1; }
+;
+
+label:
+ export T_SYMBOL ':'
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Program label multiply defined", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = LABEL;
+ initialize_symbol($2);
+ $2->info.linfo->address = instruction_ptr;
+ $2->info.linfo->exported = $1;
+ }
+;
+
+address:
+ T_SYMBOL
+ {
+ $$.symbol = $1;
+ $$.offset = 0;
+ }
+| T_SYMBOL '+' T_NUMBER
+ {
+ $$.symbol = $1;
+ $$.offset = $3;
+ }
+| T_SYMBOL '-' T_NUMBER
+ {
+ $$.symbol = $1;
+ $$.offset = -$3;
+ }
+| '.'
+ {
+ $$.symbol = NULL;
+ $$.offset = 0;
+ }
+| '.' '+' T_NUMBER
+ {
+ $$.symbol = NULL;
+ $$.offset = $3;
+ }
+| '.' '-' T_NUMBER
+ {
+ $$.symbol = NULL;
+ $$.offset = -$3;
+ }
+;
+
+conditional:
+ T_IF T_CEXPR '{'
+ {
+ scope_t *new_scope;
+
+ add_conditional($2);
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_IF;
+ new_scope->begin_addr = instruction_ptr;
+ new_scope->func_num = $2->info.condinfo->func_num;
+ }
+| T_ELSE T_IF T_CEXPR '{'
+ {
+ scope_t *new_scope;
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ /*
+ * Ensure that the previous scope is either an
+ * if or and else if.
+ */
+ scope_context = SLIST_FIRST(&scope_stack);
+ last_scope = TAILQ_LAST(&scope_context->inner_scope,
+ scope_tailq);
+ if (last_scope == NULL
+ || last_scope->type == T_ELSE) {
+
+ stop("'else if' without leading 'if'", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ add_conditional($3);
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_ELSE_IF;
+ new_scope->begin_addr = instruction_ptr;
+ new_scope->func_num = $3->info.condinfo->func_num;
+ }
+| T_ELSE '{'
+ {
+ scope_t *new_scope;
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ /*
+ * Ensure that the previous scope is either an
+ * if or and else if.
+ */
+ scope_context = SLIST_FIRST(&scope_stack);
+ last_scope = TAILQ_LAST(&scope_context->inner_scope,
+ scope_tailq);
+ if (last_scope == NULL
+ || last_scope->type == SCOPE_ELSE) {
+
+ stop("'else' without leading 'if'", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_ELSE;
+ new_scope->begin_addr = instruction_ptr;
+ }
+;
+
+conditional:
+ '}'
+ {
+ scope_t *scope_context;
+
+ scope_context = SLIST_FIRST(&scope_stack);
+ if (scope_context->type == SCOPE_ROOT) {
+ stop("Unexpected '}' encountered", EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ scope_context->end_addr = instruction_ptr;
+
+ /* Pop the scope */
+ SLIST_REMOVE_HEAD(&scope_stack, scope_stack_links);
+
+ process_scope(scope_context);
+
+ if (SLIST_FIRST(&scope_stack) == NULL) {
+ stop("Unexpected '}' encountered", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ }
+;
+
+f1_opcode:
+ T_AND { $$ = AIC_OP_AND; }
+| T_XOR { $$ = AIC_OP_XOR; }
+| T_ADD { $$ = AIC_OP_ADD; }
+| T_ADC { $$ = AIC_OP_ADC; }
+;
+
+code:
+ f1_opcode destination ',' immediate_or_a opt_source ret ';'
+ {
+ format_1_instr($1, &$2, &$4, &$5, $6);
+ }
+;
+
+code:
+ T_OR reg_symbol ',' immediate_or_a opt_source ret ';'
+ {
+ format_1_instr(AIC_OP_OR, &$2, &$4, &$5, $6);
+ }
+;
+
+code:
+ T_INC destination opt_source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+ }
+;
+
+code:
+ T_DEC destination opt_source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, -1);
+ format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+ }
+;
+
+code:
+ T_CLC ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, -1);
+ format_1_instr(AIC_OP_ADD, &none, &immed, &allzeros, $2);
+ }
+| T_CLC T_MVI destination ',' immediate_or_a ret ';'
+ {
+ format_1_instr(AIC_OP_ADD, &$3, &$5, &allzeros, $6);
+ }
+;
+
+code:
+ T_STC ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_ADD, &none, &immed, &allones, $2);
+ }
+| T_STC destination ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_ADD, &$2, &immed, &allones, $3);
+ }
+;
+
+code:
+ T_BMOV destination ',' source ',' immediate ret ';'
+ {
+ format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7);
+ }
+;
+
+code:
+ T_MOV destination ',' source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_BMOV, &$2, &immed, &$4, $5);
+ }
+;
+
+code:
+ T_MVI destination ',' immediate_or_a ret ';'
+ {
+ format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5);
+ }
+;
+
+code:
+ T_NOT destination opt_source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_XOR, &$2, &immed, &$3, $4);
+ }
+;
+
+code:
+ T_CLR destination ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &$2, &immed, &allzeros, $3);
+ }
+;
+
+code:
+ T_NOP ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2);
+ }
+;
+
+code:
+ T_RET ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, TRUE);
+ }
+;
+
+ /*
+ * This grammer differs from the one in the aic7xxx
+ * reference manual since the grammer listed there is
+ * ambiguous and causes a shift/reduce conflict.
+ * It also seems more logical as the "immediate"
+ * argument is listed as the second arg like the
+ * other formats.
+ */
+
+f2_opcode:
+ T_SHL { $$ = AIC_OP_SHL; }
+| T_SHR { $$ = AIC_OP_SHR; }
+| T_ROL { $$ = AIC_OP_ROL; }
+| T_ROR { $$ = AIC_OP_ROR; }
+;
+
+code:
+ f2_opcode destination ',' expression opt_source ret ';'
+ {
+ format_2_instr($1, &$2, &$4, &$5, $6);
+ }
+;
+
+jmp_jc_jnc_call:
+ T_JMP { $$ = AIC_OP_JMP; }
+| T_JC { $$ = AIC_OP_JC; }
+| T_JNC { $$ = AIC_OP_JNC; }
+| T_CALL { $$ = AIC_OP_CALL; }
+;
+
+jz_jnz:
+ T_JZ { $$ = AIC_OP_JZ; }
+| T_JNZ { $$ = AIC_OP_JNZ; }
+;
+
+je_jne:
+ T_JE { $$ = AIC_OP_JE; }
+| T_JNE { $$ = AIC_OP_JNE; }
+;
+
+code:
+ jmp_jc_jnc_call address ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0);
+ format_3_instr($1, &sindex, &immed, &$2);
+ }
+;
+
+code:
+ T_OR reg_symbol ',' immediate jmp_jc_jnc_call address ';'
+ {
+ format_3_instr($5, &$2, &$4, &$6);
+ }
+;
+
+code:
+ T_TEST source ',' immediate_or_a jz_jnz address ';'
+ {
+ format_3_instr($5, &$2, &$4, &$6);
+ }
+;
+
+code:
+ T_CMP source ',' immediate_or_a je_jne address ';'
+ {
+ format_3_instr($5, &$2, &$4, &$6);
+ }
+;
+
+code:
+ T_MOV source jmp_jc_jnc_call address ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0);
+ format_3_instr($3, &$2, &immed, &$4);
+ }
+;
+
+code:
+ T_MVI immediate jmp_jc_jnc_call address ';'
+ {
+ format_3_instr($3, &allzeros, &$2, &$4);
+ }
+;
+
+%%
+
+static void
+process_bitmask(int mask_type, symbol_t *sym, int mask)
+{
+ /*
+ * Add the current register to its
+ * symbol list, if it already exists,
+ * warn if we are setting it to a
+ * different value, or in the bit to
+ * the "allowed bits" of this register.
+ */
+ if (sym->type == UNINITIALIZED) {
+ sym->type = mask_type;
+ initialize_symbol(sym);
+ if (mask_type == BIT) {
+ if (mask == 0) {
+ stop("Bitmask with no bits set", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if ((mask & ~(0x01 << (ffs(mask) - 1))) != 0) {
+ stop("Bitmask with more than one bit set",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ }
+ sym->info.minfo->mask = mask;
+ } else if (sym->type != mask_type) {
+ stop("Bit definition mirrors a definition of the same "
+ " name, but a different type", EX_DATAERR);
+ /* NOTREACHED */
+ } else if (mask != sym->info.minfo->mask) {
+ stop("Bitmask redefined with a conflicting value", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ /* Fail if this symbol is already listed */
+ if (symlist_search(&(sym->info.minfo->symrefs),
+ cur_symbol->name) != NULL) {
+ stop("Bitmask defined multiple times for register", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ symlist_add(&(sym->info.minfo->symrefs), cur_symbol,
+ SYMLIST_INSERT_HEAD);
+ cur_symbol->info.rinfo->valid_bitmask |= mask;
+ cur_symbol->info.rinfo->typecheck_masks = TRUE;
+}
+
+static void
+initialize_symbol(symbol_t *symbol)
+{
+ switch (symbol->type) {
+ case UNINITIALIZED:
+ stop("Call to initialize_symbol with type field unset",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ break;
+ case REGISTER:
+ case SRAMLOC:
+ case SCBLOC:
+ symbol->info.rinfo =
+ (struct reg_info *)malloc(sizeof(struct reg_info));
+ if (symbol->info.rinfo == NULL) {
+ stop("Can't create register info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.rinfo, 0,
+ sizeof(struct reg_info));
+ /*
+ * Default to allowing access in all register modes
+ * or to the mode specified by the SCB or SRAM space
+ * we are in.
+ */
+ if (scb_or_sram_symbol != NULL)
+ symbol->info.rinfo->modes =
+ scb_or_sram_symbol->info.rinfo->modes;
+ else
+ symbol->info.rinfo->modes = ~0;
+ break;
+ case ALIAS:
+ symbol->info.ainfo =
+ (struct alias_info *)malloc(sizeof(struct alias_info));
+ if (symbol->info.ainfo == NULL) {
+ stop("Can't create alias info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.ainfo, 0,
+ sizeof(struct alias_info));
+ break;
+ case MASK:
+ case BIT:
+ symbol->info.minfo =
+ (struct mask_info *)malloc(sizeof(struct mask_info));
+ if (symbol->info.minfo == NULL) {
+ stop("Can't create bitmask info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.minfo, 0, sizeof(struct mask_info));
+ SLIST_INIT(&(symbol->info.minfo->symrefs));
+ break;
+ case CONST:
+ case DOWNLOAD_CONST:
+ symbol->info.cinfo =
+ (struct const_info *)malloc(sizeof(struct const_info));
+ if (symbol->info.cinfo == NULL) {
+ stop("Can't create alias info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.cinfo, 0,
+ sizeof(struct const_info));
+ break;
+ case LABEL:
+ symbol->info.linfo =
+ (struct label_info *)malloc(sizeof(struct label_info));
+ if (symbol->info.linfo == NULL) {
+ stop("Can't create label info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.linfo, 0,
+ sizeof(struct label_info));
+ break;
+ case CONDITIONAL:
+ symbol->info.condinfo =
+ (struct cond_info *)malloc(sizeof(struct cond_info));
+ if (symbol->info.condinfo == NULL) {
+ stop("Can't create conditional info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.condinfo, 0,
+ sizeof(struct cond_info));
+ break;
+ case MACRO:
+ symbol->info.macroinfo =
+ (struct macro_info *)malloc(sizeof(struct macro_info));
+ if (symbol->info.macroinfo == NULL) {
+ stop("Can't create macro info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.macroinfo, 0,
+ sizeof(struct macro_info));
+ STAILQ_INIT(&symbol->info.macroinfo->args);
+ break;
+ default:
+ stop("Call to initialize_symbol with invalid symbol type",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ break;
+ }
+}
+
+static void
+add_macro_arg(const char *argtext, int argnum)
+{
+ struct macro_arg *marg;
+ int i;
+ int retval;
+
+
+ if (cur_symbol == NULL || cur_symbol->type != MACRO) {
+ stop("Invalid current symbol for adding macro arg",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ marg = (struct macro_arg *)malloc(sizeof(*marg));
+ if (marg == NULL) {
+ stop("Can't create macro_arg structure", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ marg->replacement_text = NULL;
+ retval = snprintf(regex_pattern, sizeof(regex_pattern),
+ "[^-/A-Za-z0-9_](%s)([^-/A-Za-z0-9_]|$)",
+ argtext);
+ if (retval >= sizeof(regex_pattern)) {
+ stop("Regex text buffer too small for arg",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ retval = regcomp(&marg->arg_regex, regex_pattern, REG_EXTENDED);
+ if (retval != 0) {
+ stop("Regex compilation failed", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ STAILQ_INSERT_TAIL(&cur_symbol->info.macroinfo->args, marg, links);
+}
+
+static void
+add_macro_body(const char *bodytext)
+{
+ if (cur_symbol == NULL || cur_symbol->type != MACRO) {
+ stop("Invalid current symbol for adding macro arg",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ cur_symbol->info.macroinfo->body = strdup(bodytext);
+ if (cur_symbol->info.macroinfo->body == NULL) {
+ stop("Can't duplicate macro body text", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+}
+
+static void
+process_register(symbol_t **p_symbol)
+{
+ symbol_t *symbol = *p_symbol;
+
+ if (symbol->type == UNINITIALIZED) {
+ snprintf(errbuf, sizeof(errbuf), "Undefined register %s",
+ symbol->name);
+ stop(errbuf, EX_DATAERR);
+ /* NOTREACHED */
+ } else if (symbol->type == ALIAS) {
+ *p_symbol = symbol->info.ainfo->parent;
+ } else if ((symbol->type != REGISTER)
+ && (symbol->type != SCBLOC)
+ && (symbol->type != SRAMLOC)) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Specified symbol %s is not a register",
+ symbol->name);
+ stop(errbuf, EX_DATAERR);
+ }
+}
+
+static void
+format_1_instr(int opcode, symbol_ref_t *dest, expression_t *immed,
+ symbol_ref_t *src, int ret)
+{
+ struct instruction *instr;
+ struct ins_format1 *f1_instr;
+
+ if (src->symbol == NULL)
+ src = dest;
+
+ /* Test register permissions */
+ test_writable_symbol(dest->symbol);
+ test_readable_symbol(src->symbol);
+
+ /* Ensure that immediate makes sense for this destination */
+ type_check(dest->symbol, immed, opcode);
+
+ /* Allocate sequencer space for the instruction and fill it out */
+ instr = seq_alloc();
+ f1_instr = &instr->format.format1;
+ f1_instr->ret = ret ? 1 : 0;
+ f1_instr->opcode = opcode;
+ f1_instr->destination = dest->symbol->info.rinfo->address
+ + dest->offset;
+ f1_instr->source = src->symbol->info.rinfo->address
+ + src->offset;
+ f1_instr->immediate = immed->value;
+
+ if (is_download_const(immed))
+ f1_instr->parity = 1;
+ else if (dest->symbol == mode_ptr.symbol) {
+ u_int src_value;
+ u_int dst_value;
+
+ /*
+ * Attempt to update mode information if
+ * we are operating on the mode register.
+ */
+ if (src->symbol == allones.symbol)
+ src_value = 0xFF;
+ else if (src->symbol == allzeros.symbol)
+ src_value = 0;
+ else if (src->symbol == mode_ptr.symbol)
+ src_value = (dst_mode << 4) | src_mode;
+ else
+ goto cant_update;
+
+ switch (opcode) {
+ case AIC_OP_AND:
+ dst_value = src_value & immed->value;
+ break;
+ case AIC_OP_XOR:
+ dst_value = src_value ^ immed->value;
+ break;
+ case AIC_OP_ADD:
+ dst_value = (src_value + immed->value) & 0xFF;
+ break;
+ case AIC_OP_OR:
+ dst_value = src_value | immed->value;
+ break;
+ break;
+ case AIC_OP_BMOV:
+ dst_value = src_value;
+ break;
+ default:
+ goto cant_update;
+ }
+ src_mode = dst_value & 0xF;
+ dst_mode = (dst_value >> 4) & 0xF;
+cant_update:
+ }
+
+ symlist_free(&immed->referenced_syms);
+ instruction_ptr++;
+}
+
+static void
+format_2_instr(int opcode, symbol_ref_t *dest, expression_t *places,
+ symbol_ref_t *src, int ret)
+{
+ struct instruction *instr;
+ struct ins_format2 *f2_instr;
+ uint8_t shift_control;
+
+ if (src->symbol == NULL)
+ src = dest;
+
+ /* Test register permissions */
+ test_writable_symbol(dest->symbol);
+ test_readable_symbol(src->symbol);
+
+ /* Allocate sequencer space for the instruction and fill it out */
+ instr = seq_alloc();
+ f2_instr = &instr->format.format2;
+ f2_instr->ret = ret ? 1 : 0;
+ f2_instr->opcode = AIC_OP_ROL;
+ f2_instr->destination = dest->symbol->info.rinfo->address
+ + dest->offset;
+ f2_instr->source = src->symbol->info.rinfo->address
+ + src->offset;
+ if (places->value > 8 || places->value <= 0) {
+ stop("illegal shift value", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ switch (opcode) {
+ case AIC_OP_SHL:
+ if (places->value == 8)
+ shift_control = 0xf0;
+ else
+ shift_control = (places->value << 4) | places->value;
+ break;
+ case AIC_OP_SHR:
+ if (places->value == 8) {
+ shift_control = 0xf8;
+ } else {
+ shift_control = (places->value << 4)
+ | (8 - places->value)
+ | 0x08;
+ }
+ break;
+ case AIC_OP_ROL:
+ shift_control = places->value & 0x7;
+ break;
+ case AIC_OP_ROR:
+ shift_control = (8 - places->value) | 0x08;
+ break;
+ default:
+ shift_control = 0; /* Quiet Compiler */
+ stop("Invalid shift operation specified", EX_SOFTWARE);
+ /* NOTREACHED */
+ break;
+ };
+ f2_instr->shift_control = shift_control;
+ symlist_free(&places->referenced_syms);
+ instruction_ptr++;
+}
+
+static void
+format_3_instr(int opcode, symbol_ref_t *src,
+ expression_t *immed, symbol_ref_t *address)
+{
+ struct instruction *instr;
+ struct ins_format3 *f3_instr;
+ int addr;
+
+ /* Test register permissions */
+ test_readable_symbol(src->symbol);
+
+ /* Ensure that immediate makes sense for this source */
+ type_check(src->symbol, immed, opcode);
+
+ /* Allocate sequencer space for the instruction and fill it out */
+ instr = seq_alloc();
+ f3_instr = &instr->format.format3;
+ if (address->symbol == NULL) {
+ /* 'dot' referrence. Use the current instruction pointer */
+ addr = instruction_ptr + address->offset;
+ } else if (address->symbol->type == UNINITIALIZED) {
+ /* forward reference */
+ addr = address->offset;
+ instr->patch_label = address->symbol;
+ } else
+ addr = address->symbol->info.linfo->address + address->offset;
+ f3_instr->opcode = opcode;
+ f3_instr->address = addr;
+ f3_instr->source = src->symbol->info.rinfo->address
+ + src->offset;
+ f3_instr->immediate = immed->value;
+
+ if (is_download_const(immed))
+ f3_instr->parity = 1;
+
+ symlist_free(&immed->referenced_syms);
+ instruction_ptr++;
+}
+
+static void
+test_readable_symbol(symbol_t *symbol)
+{
+
+ if ((symbol->info.rinfo->modes & (0x1 << src_mode)) == 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Register %s unavailable in source reg mode %d",
+ symbol->name, src_mode);
+ stop(errbuf, EX_DATAERR);
+ }
+
+ if (symbol->info.rinfo->mode == WO) {
+ stop("Write Only register specified as source",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+}
+
+static void
+test_writable_symbol(symbol_t *symbol)
+{
+
+ if ((symbol->info.rinfo->modes & (0x1 << dst_mode)) == 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Register %s unavailable in destination reg mode %d",
+ symbol->name, dst_mode);
+ stop(errbuf, EX_DATAERR);
+ }
+
+ if (symbol->info.rinfo->mode == RO) {
+ stop("Read Only register specified as destination",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+}
+
+static void
+type_check(symbol_t *symbol, expression_t *expression, int opcode)
+{
+ symbol_node_t *node;
+ int and_op;
+
+ and_op = FALSE;
+ if (opcode == AIC_OP_AND || opcode == AIC_OP_JNZ || AIC_OP_JZ)
+ and_op = TRUE;
+
+ /*
+ * Make sure that we aren't attempting to write something
+ * that hasn't been defined. If this is an and operation,
+ * this is a mask, so "undefined" bits are okay.
+ */
+ if (and_op == FALSE
+ && (expression->value & ~symbol->info.rinfo->valid_bitmask) != 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Invalid bit(s) 0x%x in immediate written to %s",
+ expression->value & ~symbol->info.rinfo->valid_bitmask,
+ symbol->name);
+ stop(errbuf, EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ /*
+ * Now make sure that all of the symbols referenced by the
+ * expression are defined for this register.
+ */
+ if (symbol->info.rinfo->typecheck_masks != FALSE) {
+ for(node = expression->referenced_syms.slh_first;
+ node != NULL;
+ node = node->links.sle_next) {
+ if ((node->symbol->type == MASK
+ || node->symbol->type == BIT)
+ && symlist_search(&node->symbol->info.minfo->symrefs,
+ symbol->name) == NULL) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Invalid bit or mask %s "
+ "for register %s",
+ node->symbol->name, symbol->name);
+ stop(errbuf, EX_DATAERR);
+ /* NOTREACHED */
+ }
+ }
+ }
+}
+
+static void
+make_expression(expression_t *immed, int value)
+{
+ SLIST_INIT(&immed->referenced_syms);
+ immed->value = value & 0xff;
+}
+
+static void
+add_conditional(symbol_t *symbol)
+{
+ static int numfuncs;
+
+ if (numfuncs == 0) {
+ /* add a special conditional, "0" */
+ symbol_t *false_func;
+
+ false_func = symtable_get("0");
+ if (false_func->type != UNINITIALIZED) {
+ stop("Conditional expression '0' "
+ "conflicts with a symbol", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ false_func->type = CONDITIONAL;
+ initialize_symbol(false_func);
+ false_func->info.condinfo->func_num = numfuncs++;
+ symlist_add(&patch_functions, false_func, SYMLIST_INSERT_HEAD);
+ }
+
+ /* This condition has occurred before */
+ if (symbol->type == CONDITIONAL)
+ return;
+
+ if (symbol->type != UNINITIALIZED) {
+ stop("Conditional expression conflicts with a symbol",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ symbol->type = CONDITIONAL;
+ initialize_symbol(symbol);
+ symbol->info.condinfo->func_num = numfuncs++;
+ symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD);
+}
+
+static void
+add_version(const char *verstring)
+{
+ const char prefix[] = " * ";
+ int newlen;
+ int oldlen;
+
+ newlen = strlen(verstring) + strlen(prefix);
+ oldlen = 0;
+ if (versions != NULL)
+ oldlen = strlen(versions);
+ versions = realloc(versions, newlen + oldlen + 2);
+ if (versions == NULL)
+ stop("Can't allocate version string", EX_SOFTWARE);
+ strcpy(&versions[oldlen], prefix);
+ strcpy(&versions[oldlen + strlen(prefix)], verstring);
+ versions[newlen + oldlen] = '\n';
+ versions[newlen + oldlen + 1] = '\0';
+}
+
+void
+yyerror(const char *string)
+{
+ stop(string, EX_DATAERR);
+}
+
+static int
+is_download_const(expression_t *immed)
+{
+ if ((immed->referenced_syms.slh_first != NULL)
+ && (immed->referenced_syms.slh_first->symbol->type == DOWNLOAD_CONST))
+ return (TRUE);
+
+ return (FALSE);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
new file mode 100644
index 0000000000..aeba6dacbb
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
@@ -0,0 +1,131 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_insformat.h#9 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_insformat.h,v 1.3.2.3 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+struct ins_format1 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t immediate : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ immediate : 8;
+#endif
+};
+
+struct ins_format2 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t shift_control : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ shift_control : 8;
+#endif
+};
+
+struct ins_format3 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t immediate : 8,
+ source : 9,
+ address : 10,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ address : 10,
+ source : 9,
+ immediate : 8;
+#endif
+};
+
+union ins_formats {
+ struct ins_format1 format1;
+ struct ins_format2 format2;
+ struct ins_format3 format3;
+ uint8_t bytes[4];
+ uint32_t integer;
+};
+struct instruction {
+ union ins_formats format;
+ u_int srcline;
+ struct symbol *patch_label;
+ STAILQ_ENTRY(instruction) links;
+};
+
+#define AIC_OP_OR 0x0
+#define AIC_OP_AND 0x1
+#define AIC_OP_XOR 0x2
+#define AIC_OP_ADD 0x3
+#define AIC_OP_ADC 0x4
+#define AIC_OP_ROL 0x5
+#define AIC_OP_BMOV 0x6
+
+#define AIC_OP_JMP 0x8
+#define AIC_OP_JC 0x9
+#define AIC_OP_JNC 0xa
+#define AIC_OP_CALL 0xb
+#define AIC_OP_JNE 0xc
+#define AIC_OP_JNZ 0xd
+#define AIC_OP_JE 0xe
+#define AIC_OP_JZ 0xf
+
+/* Pseudo Ops */
+#define AIC_OP_SHL 0x10
+#define AIC_OP_SHR 0x20
+#define AIC_OP_ROR 0x30
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
new file mode 100644
index 0000000000..dabc6c2f2f
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
@@ -0,0 +1,164 @@
+%{
+/*
+ * Sub-parser for macro invocation in the Aic7xxx SCSI
+ * Host adapter sequencer assembler.
+ *
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id$
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_macro_gram.y,v 1.1.2.1 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+static symbol_t *macro_symbol;
+
+static void add_macro_arg(const char *argtext, int position);
+
+%}
+
+%union {
+ int value;
+ char *str;
+ symbol_t *sym;
+}
+
+
+%token <str> T_ARG
+
+%token <sym> T_SYMBOL
+
+%type <value> macro_arglist
+
+%%
+
+macrocall:
+ T_SYMBOL '('
+ {
+ macro_symbol = $1;
+ }
+ macro_arglist ')'
+ {
+ if (macro_symbol->info.macroinfo->narg != $4) {
+ printf("Narg == %d", macro_symbol->info.macroinfo->narg);
+ stop("Too few arguments for macro invocation",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ macro_symbol = NULL;
+ YYACCEPT;
+ }
+;
+
+macro_arglist:
+ {
+ /* Macros can take 0 arguments */
+ $$ = 0;
+ }
+| T_ARG
+ {
+ $$ = 1;
+ add_macro_arg($1, 1);
+ }
+| macro_arglist ',' T_ARG
+ {
+ if ($1 == 0) {
+ stop("Comma without preceeding argument in arg list",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$ = $1 + 1;
+ add_macro_arg($3, $$);
+ }
+;
+
+%%
+
+static void
+add_macro_arg(const char *argtext, int argnum)
+{
+ struct macro_arg *marg;
+ int i;
+
+ if (macro_symbol == NULL || macro_symbol->type != MACRO) {
+ stop("Invalid current symbol for adding macro arg",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ /*
+ * Macro Invocation. Find the appropriate argument and fill
+ * in the replace ment text for this call.
+ */
+ i = 0;
+ STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
+ i++;
+ if (i == argnum)
+ break;
+ }
+ if (marg == NULL) {
+ stop("Too many arguments for macro invocation", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ marg->replacement_text = strdup(argtext);
+ if (marg->replacement_text == NULL) {
+ stop("Unable to replicate replacement text", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+}
+
+void
+mmerror(const char *string)
+{
+ stop(string, EX_DATAERR);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
new file mode 100644
index 0000000000..c1bf57efbb
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
@@ -0,0 +1,154 @@
+%{
+/*
+ * Sub-Lexical Analyzer for macro invokation in
+ * the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id$
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_macro_scan.l,v 1.1.2.1 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_macro_gram.h"
+
+#define MAX_STR_CONST 4096
+static char string_buf[MAX_STR_CONST];
+static char *string_buf_ptr;
+static int parren_count;
+static char buf[255];
+%}
+
+WORD [A-Za-z_][-A-Za-z_0-9]*
+SPACE [ \t]+
+MCARG [^(), \t]+
+
+%x ARGLIST
+
+%%
+\n {
+ ++yylineno;
+ }
+<ARGLIST>{SPACE} ;
+<ARGLIST>\( {
+ parren_count++;
+ if (parren_count == 1) {
+ string_buf_ptr = string_buf;
+ return ('(');
+ }
+ *string_buf_ptr++ = '(';
+ }
+<ARGLIST>\) {
+ if (parren_count == 1) {
+ if (string_buf_ptr != string_buf) {
+ /*
+ * Return an argument and
+ * rescan this parren so we
+ * can return it as well.
+ */
+ *string_buf_ptr = '\0';
+ mmlval.str = string_buf;
+ string_buf_ptr = string_buf;
+ unput(')');
+ return T_ARG;
+ }
+ BEGIN INITIAL;
+ return (')');
+ }
+ parren_count--;
+ *string_buf_ptr++ = ')';
+ }
+<ARGLIST>{MCARG} {
+ char *yptr;
+
+ yptr = mmtext;
+ while (*yptr)
+ *string_buf_ptr++ = *yptr++;
+ }
+<ARGLIST>\, {
+ if (string_buf_ptr != string_buf) {
+ /*
+ * Return an argument and
+ * rescan this comma so we
+ * can return it as well.
+ */
+ *string_buf_ptr = '\0';
+ mmlval.str = string_buf;
+ string_buf_ptr = string_buf;
+ unput(',');
+ return T_ARG;
+ }
+ return ',';
+ }
+{WORD}[(] {
+ /* May be a symbol or a macro invocation. */
+ mmlval.sym = symtable_get(mmtext);
+ if (mmlval.sym->type != MACRO) {
+ stop("Expecting Macro Name",
+ EX_DATAERR);
+ }
+ unput('(');
+ parren_count = 0;
+ BEGIN ARGLIST;
+ return T_SYMBOL;
+ }
+. {
+ snprintf(buf, sizeof(buf), "Invalid character "
+ "'%c'", mmtext[0]);
+ stop(buf, EX_DATAERR);
+ }
+%%
+
+int
+mmwrap()
+{
+ stop("EOF encountered in macro call", EX_DATAERR);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
new file mode 100644
index 0000000000..024c5765cd
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
@@ -0,0 +1,594 @@
+%{
+/*
+ * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_scan.l#11 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_scan.l,v 1.13.2.4 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_gram.h"
+
+/* This is used for macro body capture too, so err on the large size. */
+#define MAX_STR_CONST 4096
+static char string_buf[MAX_STR_CONST];
+static char *string_buf_ptr;
+static int parren_count;
+static int quote_count;
+static char buf[255];
+%}
+
+PATH ([/]*[-A-Za-z0-9_.])+
+WORD [A-Za-z_][-A-Za-z_0-9]*
+SPACE [ \t]+
+MCARG [^(), \t]+
+MBODY ((\\[^\n])*[^\n\\]*)+
+
+%x COMMENT
+%x CEXPR
+%x INCLUDE
+%x STRING
+%x MACRODEF
+%x MACROARGLIST
+%x MACROCALLARGS
+%x MACROBODY
+
+%%
+\n { ++yylineno; }
+"/*" { BEGIN COMMENT; /* Enter comment eating state */ }
+<COMMENT>"/*" { fprintf(stderr, "Warning! Comment within comment."); }
+<COMMENT>\n { ++yylineno; }
+<COMMENT>[^*/\n]* ;
+<COMMENT>"*"+[^*/\n]* ;
+<COMMENT>"/"+[^*/\n]* ;
+<COMMENT>"*"+"/" { BEGIN INITIAL; }
+if[ \t]*\( {
+ string_buf_ptr = string_buf;
+ parren_count = 1;
+ BEGIN CEXPR;
+ return T_IF;
+ }
+<CEXPR>\( { *string_buf_ptr++ = '('; parren_count++; }
+<CEXPR>\) {
+ parren_count--;
+ if (parren_count == 0) {
+ /* All done */
+ BEGIN INITIAL;
+ *string_buf_ptr = '\0';
+ yylval.sym = symtable_get(string_buf);
+ return T_CEXPR;
+ } else {
+ *string_buf_ptr++ = ')';
+ }
+ }
+<CEXPR>\n { ++yylineno; }
+<CEXPR>[^()\n]+ {
+ char *yptr;
+
+ yptr = yytext;
+ while (*yptr != '\0') {
+ /* Remove duplicate spaces */
+ if (*yptr == '\t')
+ *yptr = ' ';
+ if (*yptr == ' '
+ && string_buf_ptr != string_buf
+ && string_buf_ptr[-1] == ' ')
+ yptr++;
+ else
+ *string_buf_ptr++ = *yptr++;
+ }
+ }
+
+VERSION { return T_VERSION; }
+PATCH_ARG_LIST { return T_PATCH_ARG_LIST; }
+\" {
+ string_buf_ptr = string_buf;
+ BEGIN STRING;
+ }
+<STRING>[^"]+ {
+ char *yptr;
+
+ yptr = yytext;
+ while (*yptr)
+ *string_buf_ptr++ = *yptr++;
+ }
+<STRING>\" {
+ /* All done */
+ BEGIN INITIAL;
+ *string_buf_ptr = '\0';
+ yylval.str = string_buf;
+ return T_STRING;
+ }
+{SPACE} ;
+
+ /* Register/SCB/SRAM definition keywords */
+export { return T_EXPORT; }
+register { return T_REGISTER; }
+const { yylval.value = FALSE; return T_CONST; }
+download { return T_DOWNLOAD; }
+address { return T_ADDRESS; }
+access_mode { return T_ACCESS_MODE; }
+modes { return T_MODES; }
+RW|RO|WO {
+ if (strcmp(yytext, "RW") == 0)
+ yylval.value = RW;
+ else if (strcmp(yytext, "RO") == 0)
+ yylval.value = RO;
+ else
+ yylval.value = WO;
+ return T_MODE;
+ }
+BEGIN_CRITICAL { return T_BEGIN_CS; }
+END_CRITICAL { return T_END_CS; }
+SET_SRC_MODE { return T_SET_SRC_MODE; }
+SET_DST_MODE { return T_SET_DST_MODE; }
+bit { return T_BIT; }
+mask { return T_MASK; }
+alias { return T_ALIAS; }
+size { return T_SIZE; }
+scb { return T_SCB; }
+scratch_ram { return T_SRAM; }
+accumulator { return T_ACCUM; }
+mode_pointer { return T_MODE_PTR; }
+allones { return T_ALLONES; }
+allzeros { return T_ALLZEROS; }
+none { return T_NONE; }
+sindex { return T_SINDEX; }
+A { return T_A; }
+
+ /* Opcodes */
+shl { return T_SHL; }
+shr { return T_SHR; }
+ror { return T_ROR; }
+rol { return T_ROL; }
+mvi { return T_MVI; }
+mov { return T_MOV; }
+clr { return T_CLR; }
+jmp { return T_JMP; }
+jc { return T_JC; }
+jnc { return T_JNC; }
+je { return T_JE; }
+jne { return T_JNE; }
+jz { return T_JZ; }
+jnz { return T_JNZ; }
+call { return T_CALL; }
+add { return T_ADD; }
+adc { return T_ADC; }
+bmov { return T_BMOV; }
+inc { return T_INC; }
+dec { return T_DEC; }
+stc { return T_STC; }
+clc { return T_CLC; }
+cmp { return T_CMP; }
+not { return T_NOT; }
+xor { return T_XOR; }
+test { return T_TEST;}
+and { return T_AND; }
+or { return T_OR; }
+ret { return T_RET; }
+nop { return T_NOP; }
+else { return T_ELSE; }
+
+ /* Allowed Symbols */
+\<\< { return T_EXPR_LSHIFT; }
+\>\> { return T_EXPR_RSHIFT; }
+[-+,:()~|&."{};<>[\]/*!=] { return yytext[0]; }
+
+ /* Number processing */
+0[0-7]* {
+ yylval.value = strtol(yytext, NULL, 8);
+ return T_NUMBER;
+ }
+
+0[xX][0-9a-fA-F]+ {
+ yylval.value = strtoul(yytext + 2, NULL, 16);
+ return T_NUMBER;
+ }
+
+[1-9][0-9]* {
+ yylval.value = strtol(yytext, NULL, 10);
+ return T_NUMBER;
+ }
+ /* Include Files */
+#include{SPACE} {
+ BEGIN INCLUDE;
+ quote_count = 0;
+ return T_INCLUDE;
+ }
+<INCLUDE>[<] { return yytext[0]; }
+<INCLUDE>[>] { BEGIN INITIAL; return yytext[0]; }
+<INCLUDE>[\"] {
+ if (quote_count != 0)
+ BEGIN INITIAL;
+ quote_count++;
+ return yytext[0];
+ }
+<INCLUDE>{PATH} {
+ char *yptr;
+
+ yptr = yytext;
+ string_buf_ptr = string_buf;
+ while (*yptr)
+ *string_buf_ptr++ = *yptr++;
+ yylval.str = string_buf;
+ *string_buf_ptr = '\0';
+ return T_PATH;
+ }
+<INCLUDE>. { stop("Invalid include line", EX_DATAERR); }
+#define{SPACE} {
+ BEGIN MACRODEF;
+ return T_DEFINE;
+ }
+<MACRODEF>{WORD}{SPACE} {
+ char *yptr;
+
+ /* Strip space and return as a normal symbol */
+ yptr = yytext;
+ while (*yptr != ' ' && *yptr != '\t')
+ yptr++;
+ *yptr = '\0';
+ yylval.sym = symtable_get(yytext);
+ string_buf_ptr = string_buf;
+ BEGIN MACROBODY;
+ return T_SYMBOL;
+ }
+<MACRODEF>{WORD}\( {
+ /*
+ * We store the symbol with its opening
+ * parren so we can differentiate macros
+ * that take args from macros with the
+ * same name that do not take args as
+ * is allowed in C.
+ */
+ BEGIN MACROARGLIST;
+ yylval.sym = symtable_get(yytext);
+ unput('(');
+ return T_SYMBOL;
+ }
+<MACROARGLIST>{WORD} {
+ yylval.str = yytext;
+ return T_ARG;
+ }
+<MACROARGLIST>{SPACE} ;
+<MACROARGLIST>[(,] {
+ return yytext[0];
+ }
+<MACROARGLIST>[)] {
+ string_buf_ptr = string_buf;
+ BEGIN MACROBODY;
+ return ')';
+ }
+<MACROARGLIST>. {
+ snprintf(buf, sizeof(buf), "Invalid character "
+ "'%c' in macro argument list",
+ yytext[0]);
+ stop(buf, EX_DATAERR);
+ }
+<MACROCALLARGS>{SPACE} ;
+<MACROCALLARGS>\( {
+ parren_count++;
+ if (parren_count == 1)
+ return ('(');
+ *string_buf_ptr++ = '(';
+ }
+<MACROCALLARGS>\) {
+ parren_count--;
+ if (parren_count == 0) {
+ BEGIN INITIAL;
+ return (')');
+ }
+ *string_buf_ptr++ = ')';
+ }
+<MACROCALLARGS>{MCARG} {
+ char *yptr;
+
+ yptr = yytext;
+ while (*yptr)
+ *string_buf_ptr++ = *yptr++;
+ }
+<MACROCALLARGS>\, {
+ if (string_buf_ptr != string_buf) {
+ /*
+ * Return an argument and
+ * rescan this comma so we
+ * can return it as well.
+ */
+ *string_buf_ptr = '\0';
+ yylval.str = string_buf;
+ string_buf_ptr = string_buf;
+ unput(',');
+ return T_ARG;
+ }
+ return ',';
+ }
+<MACROBODY>\\\n {
+ /* Eat escaped newlines. */
+ ++yylineno;
+ }
+<MACROBODY>\n {
+ /* Macros end on the first unescaped newline. */
+ BEGIN INITIAL;
+ *string_buf_ptr = '\0';
+ yylval.str = string_buf;
+ ++yylineno;
+ return T_MACROBODY;
+ }
+<MACROBODY>{MBODY} {
+ char *yptr;
+
+ yptr = yytext;
+ while (*yptr)
+ *string_buf_ptr++ = *yptr++;
+ }
+{WORD}\( {
+ char *yptr;
+ char *ycopy;
+
+ /* May be a symbol or a macro invocation. */
+ yylval.sym = symtable_get(yytext);
+ if (yylval.sym->type == MACRO) {
+ YY_BUFFER_STATE old_state;
+ YY_BUFFER_STATE temp_state;
+
+ ycopy = strdup(yytext);
+ yptr = ycopy + yyleng;
+ while (yptr > ycopy)
+ unput(*--yptr);
+ old_state = YY_CURRENT_BUFFER;
+ temp_state =
+ yy_create_buffer(stdin,
+ YY_BUF_SIZE);
+ yy_switch_to_buffer(temp_state);
+ mm_switch_to_buffer(old_state);
+ mmparse();
+ mm_switch_to_buffer(temp_state);
+ yy_switch_to_buffer(old_state);
+ mm_delete_buffer(temp_state);
+ expand_macro(yylval.sym);
+ } else {
+ if (yylval.sym->type == UNINITIALIZED) {
+ /* Try without the '(' */
+ symbol_delete(yylval.sym);
+ yytext[yyleng-1] = '\0';
+ yylval.sym =
+ symtable_get(yytext);
+ }
+ unput('(');
+ return T_SYMBOL;
+ }
+ }
+{WORD} {
+ yylval.sym = symtable_get(yytext);
+ if (yylval.sym->type == MACRO) {
+ expand_macro(yylval.sym);
+ } else {
+ return T_SYMBOL;
+ }
+ }
+. {
+ snprintf(buf, sizeof(buf), "Invalid character "
+ "'%c'", yytext[0]);
+ stop(buf, EX_DATAERR);
+ }
+%%
+
+typedef struct include {
+ YY_BUFFER_STATE buffer;
+ int lineno;
+ char *filename;
+ SLIST_ENTRY(include) links;
+}include_t;
+
+SLIST_HEAD(, include) include_stack;
+
+void
+include_file(char *file_name, include_type type)
+{
+ FILE *newfile;
+ include_t *include;
+
+ newfile = NULL;
+ /* Try the current directory first */
+ if (includes_search_curdir != 0 || type == SOURCE_FILE)
+ newfile = fopen(file_name, "r");
+
+ if (newfile == NULL && type != SOURCE_FILE) {
+ path_entry_t include_dir;
+ for (include_dir = search_path.slh_first;
+ include_dir != NULL;
+ include_dir = include_dir->links.sle_next) {
+ char fullname[PATH_MAX];
+
+ if ((include_dir->quoted_includes_only == TRUE)
+ && (type != QUOTED_INCLUDE))
+ continue;
+
+ snprintf(fullname, sizeof(fullname),
+ "%s/%s", include_dir->directory, file_name);
+
+ if ((newfile = fopen(fullname, "r")) != NULL)
+ break;
+ }
+ }
+
+ if (newfile == NULL) {
+ perror(file_name);
+ stop("Unable to open input file", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ if (type != SOURCE_FILE) {
+ include = (include_t *)malloc(sizeof(include_t));
+ if (include == NULL) {
+ stop("Unable to allocate include stack entry",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ include->buffer = YY_CURRENT_BUFFER;
+ include->lineno = yylineno;
+ include->filename = yyfilename;
+ SLIST_INSERT_HEAD(&include_stack, include, links);
+ }
+ yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE));
+ yylineno = 1;
+ yyfilename = strdup(file_name);
+}
+
+static void next_substitution(struct symbol *mac_symbol, const char *body_pos,
+ const char **next_match,
+ struct macro_arg **match_marg, regmatch_t *match);
+
+void
+expand_macro(struct symbol *macro_symbol)
+{
+ struct macro_arg *marg;
+ struct macro_arg *match_marg;
+ const char *body_head;
+ const char *body_pos;
+ const char *next_match;
+
+ /*
+ * Due to the nature of unput, we must work
+ * backwards through the macro body performing
+ * any expansions.
+ */
+ body_head = macro_symbol->info.macroinfo->body;
+ body_pos = body_head + strlen(body_head);
+ while (body_pos > body_head) {
+ regmatch_t match;
+
+ next_match = body_head;
+ match_marg = NULL;
+ next_substitution(macro_symbol, body_pos, &next_match,
+ &match_marg, &match);
+
+ /* Put back everything up until the replacement. */
+ while (body_pos > next_match)
+ unput(*--body_pos);
+
+ /* Perform the replacement. */
+ if (match_marg != NULL) {
+ const char *strp;
+
+ next_match = match_marg->replacement_text;
+ strp = next_match + strlen(next_match);
+ while (strp > next_match)
+ unput(*--strp);
+
+ /* Skip past the unexpanded macro arg. */
+ body_pos -= match.rm_eo - match.rm_so;
+ }
+ }
+
+ /* Cleanup replacement text. */
+ STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
+ free(marg->replacement_text);
+ }
+}
+
+/*
+ * Find the next substitution in the macro working backwards from
+ * body_pos until the beginning of the macro buffer. next_match
+ * should be initialized to the beginning of the macro buffer prior
+ * to calling this routine.
+ */
+static void
+next_substitution(struct symbol *mac_symbol, const char *body_pos,
+ const char **next_match, struct macro_arg **match_marg,
+ regmatch_t *match)
+{
+ regmatch_t matches[2];
+ struct macro_arg *marg;
+ const char *search_pos;
+ int retval;
+
+ do {
+ search_pos = *next_match;
+
+ STAILQ_FOREACH(marg, &mac_symbol->info.macroinfo->args, links) {
+
+ retval = regexec(&marg->arg_regex, search_pos, 2,
+ matches, 0);
+ if (retval == 0
+ && (matches[1].rm_eo + search_pos) <= body_pos
+ && (matches[1].rm_eo + search_pos) > *next_match) {
+ *match = matches[1];
+ *next_match = match->rm_eo + search_pos;
+ *match_marg = marg;
+ }
+ }
+ } while (search_pos != *next_match);
+}
+
+int
+yywrap()
+{
+ include_t *include;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+ (void)fclose(yyin);
+ if (yyfilename != NULL)
+ free(yyfilename);
+ yyfilename = NULL;
+ include = include_stack.slh_first;
+ if (include != NULL) {
+ yy_switch_to_buffer(include->buffer);
+ yylineno = include->lineno;
+ yyfilename = include->filename;
+ SLIST_REMOVE_HEAD(&include_stack, links);
+ free(include);
+ return (0);
+ }
+ return (1);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
new file mode 100644
index 0000000000..acb356004d
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
@@ -0,0 +1,500 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table implementation
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.c#14 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_symbol.c,v 1.11.2.4 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#ifdef __linux__
+#include "aicdb.h"
+#else
+#include <db.h>
+#endif
+#include <fcntl.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "aicasm_symbol.h"
+#include "aicasm.h"
+
+static DB *symtable;
+
+symbol_t *
+symbol_create(char *name)
+{
+ symbol_t *new_symbol;
+
+ new_symbol = (symbol_t *)malloc(sizeof(symbol_t));
+ if (new_symbol == NULL) {
+ perror("Unable to create new symbol");
+ exit(EX_SOFTWARE);
+ }
+ memset(new_symbol, 0, sizeof(*new_symbol));
+ new_symbol->name = strdup(name);
+ new_symbol->type = UNINITIALIZED;
+ return (new_symbol);
+}
+
+void
+symbol_delete(symbol_t *symbol)
+{
+ if (symtable != NULL) {
+ DBT key;
+
+ key.data = symbol->name;
+ key.size = strlen(symbol->name);
+ symtable->del(symtable, &key, /*flags*/0);
+ }
+ switch(symbol->type) {
+ case SCBLOC:
+ case SRAMLOC:
+ case REGISTER:
+ if (symbol->info.rinfo != NULL)
+ free(symbol->info.rinfo);
+ break;
+ case ALIAS:
+ if (symbol->info.ainfo != NULL)
+ free(symbol->info.ainfo);
+ break;
+ case MASK:
+ case BIT:
+ if (symbol->info.minfo != NULL) {
+ symlist_free(&symbol->info.minfo->symrefs);
+ free(symbol->info.minfo);
+ }
+ break;
+ case DOWNLOAD_CONST:
+ case CONST:
+ if (symbol->info.cinfo != NULL)
+ free(symbol->info.cinfo);
+ break;
+ case LABEL:
+ if (symbol->info.linfo != NULL)
+ free(symbol->info.linfo);
+ break;
+ case UNINITIALIZED:
+ default:
+ break;
+ }
+ free(symbol->name);
+ free(symbol);
+}
+
+void
+symtable_open()
+{
+ symtable = dbopen(/*filename*/NULL,
+ O_CREAT | O_NONBLOCK | O_RDWR, /*mode*/0, DB_HASH,
+ /*openinfo*/NULL);
+
+ if (symtable == NULL) {
+ perror("Symbol table creation failed");
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+}
+
+void
+symtable_close()
+{
+ if (symtable != NULL) {
+ DBT key;
+ DBT data;
+
+ while (symtable->seq(symtable, &key, &data, R_FIRST) == 0) {
+ symbol_t *stored_ptr;
+
+ memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+ symbol_delete(stored_ptr);
+ }
+ symtable->close(symtable);
+ }
+}
+
+/*
+ * The semantics of get is to return an uninitialized symbol entry
+ * if a lookup fails.
+ */
+symbol_t *
+symtable_get(char *name)
+{
+ symbol_t *stored_ptr;
+ DBT key;
+ DBT data;
+ int retval;
+
+ key.data = (void *)name;
+ key.size = strlen(name);
+
+ if ((retval = symtable->get(symtable, &key, &data, /*flags*/0)) != 0) {
+ if (retval == -1) {
+ perror("Symbol table get operation failed");
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ } else if (retval == 1) {
+ /* Symbol wasn't found, so create a new one */
+ symbol_t *new_symbol;
+
+ new_symbol = symbol_create(name);
+ data.data = &new_symbol;
+ data.size = sizeof(new_symbol);
+ if (symtable->put(symtable, &key, &data,
+ /*flags*/0) !=0) {
+ perror("Symtable put failed");
+ exit(EX_SOFTWARE);
+ }
+ return (new_symbol);
+ } else {
+ perror("Unexpected return value from db get routine");
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ }
+ memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+ return (stored_ptr);
+}
+
+symbol_node_t *
+symlist_search(symlist_t *symlist, char *symname)
+{
+ symbol_node_t *curnode;
+
+ curnode = SLIST_FIRST(symlist);
+ while(curnode != NULL) {
+ if (strcmp(symname, curnode->symbol->name) == 0)
+ break;
+ curnode = SLIST_NEXT(curnode, links);
+ }
+ return (curnode);
+}
+
+void
+symlist_add(symlist_t *symlist, symbol_t *symbol, int how)
+{
+ symbol_node_t *newnode;
+
+ newnode = (symbol_node_t *)malloc(sizeof(symbol_node_t));
+ if (newnode == NULL) {
+ stop("symlist_add: Unable to malloc symbol_node", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ newnode->symbol = symbol;
+ if (how == SYMLIST_SORT) {
+ symbol_node_t *curnode;
+ int mask;
+
+ mask = FALSE;
+ switch(symbol->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ break;
+ case BIT:
+ case MASK:
+ mask = TRUE;
+ break;
+ default:
+ stop("symlist_add: Invalid symbol type for sorting",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ curnode = SLIST_FIRST(symlist);
+ if (curnode == NULL
+ || (mask && (curnode->symbol->info.minfo->mask >
+ newnode->symbol->info.minfo->mask))
+ || (!mask && (curnode->symbol->info.rinfo->address >
+ newnode->symbol->info.rinfo->address))) {
+ SLIST_INSERT_HEAD(symlist, newnode, links);
+ return;
+ }
+
+ while (1) {
+ if (SLIST_NEXT(curnode, links) == NULL) {
+ SLIST_INSERT_AFTER(curnode, newnode,
+ links);
+ break;
+ } else {
+ symbol_t *cursymbol;
+
+ cursymbol = SLIST_NEXT(curnode, links)->symbol;
+ if ((mask && (cursymbol->info.minfo->mask >
+ symbol->info.minfo->mask))
+ || (!mask &&(cursymbol->info.rinfo->address >
+ symbol->info.rinfo->address))){
+ SLIST_INSERT_AFTER(curnode, newnode,
+ links);
+ break;
+ }
+ }
+ curnode = SLIST_NEXT(curnode, links);
+ }
+ } else {
+ SLIST_INSERT_HEAD(symlist, newnode, links);
+ }
+}
+
+void
+symlist_free(symlist_t *symlist)
+{
+ symbol_node_t *node1, *node2;
+
+ node1 = SLIST_FIRST(symlist);
+ while (node1 != NULL) {
+ node2 = SLIST_NEXT(node1, links);
+ free(node1);
+ node1 = node2;
+ }
+ SLIST_INIT(symlist);
+}
+
+void
+symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
+ symlist_t *symlist_src2)
+{
+ symbol_node_t *node;
+
+ *symlist_dest = *symlist_src1;
+ while((node = SLIST_FIRST(symlist_src2)) != NULL) {
+ SLIST_REMOVE_HEAD(symlist_src2, links);
+ SLIST_INSERT_HEAD(symlist_dest, node, links);
+ }
+
+ /* These are now empty */
+ SLIST_INIT(symlist_src1);
+ SLIST_INIT(symlist_src2);
+}
+
+void
+symtable_dump(FILE *ofile)
+{
+ /*
+ * Sort the registers by address with a simple insertion sort.
+ * Put bitmasks next to the first register that defines them.
+ * Put constants at the end.
+ */
+ symlist_t registers;
+ symlist_t masks;
+ symlist_t constants;
+ symlist_t download_constants;
+ symlist_t aliases;
+ symlist_t exported_labels;
+ u_int i;
+
+ SLIST_INIT(&registers);
+ SLIST_INIT(&masks);
+ SLIST_INIT(&constants);
+ SLIST_INIT(&download_constants);
+ SLIST_INIT(&aliases);
+ SLIST_INIT(&exported_labels);
+
+ if (symtable != NULL) {
+ DBT key;
+ DBT data;
+ int flag = R_FIRST;
+
+ while (symtable->seq(symtable, &key, &data, flag) == 0) {
+ symbol_t *cursym;
+
+ memcpy(&cursym, data.data, sizeof(cursym));
+ switch(cursym->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ symlist_add(&registers, cursym, SYMLIST_SORT);
+ break;
+ case MASK:
+ case BIT:
+ symlist_add(&masks, cursym, SYMLIST_SORT);
+ break;
+ case CONST:
+ symlist_add(&constants, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ case DOWNLOAD_CONST:
+ symlist_add(&download_constants, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ case ALIAS:
+ symlist_add(&aliases, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ case LABEL:
+ if (cursym->info.linfo->exported == 0)
+ break;
+ symlist_add(&exported_labels, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ default:
+ break;
+ }
+ flag = R_NEXT;
+ }
+
+ /* Put in the masks and bits */
+ while (SLIST_FIRST(&masks) != NULL) {
+ symbol_node_t *curnode;
+ symbol_node_t *regnode;
+ char *regname;
+
+ curnode = SLIST_FIRST(&masks);
+ SLIST_REMOVE_HEAD(&masks, links);
+
+ regnode =
+ SLIST_FIRST(&curnode->symbol->info.minfo->symrefs);
+ regname = regnode->symbol->name;
+ regnode = symlist_search(&registers, regname);
+ SLIST_INSERT_AFTER(regnode, curnode, links);
+ }
+
+ /* Add the aliases */
+ while (SLIST_FIRST(&aliases) != NULL) {
+ symbol_node_t *curnode;
+ symbol_node_t *regnode;
+ char *regname;
+
+ curnode = SLIST_FIRST(&aliases);
+ SLIST_REMOVE_HEAD(&aliases, links);
+
+ regname = curnode->symbol->info.ainfo->parent->name;
+ regnode = symlist_search(&registers, regname);
+ SLIST_INSERT_AFTER(regnode, curnode, links);
+ }
+
+ /* Output what we have */
+ fprintf(ofile,
+"/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+%s */\n", versions);
+ while (SLIST_FIRST(&registers) != NULL) {
+ symbol_node_t *curnode;
+ u_int value;
+ char *tab_str;
+ char *tab_str2;
+
+ curnode = SLIST_FIRST(&registers);
+ SLIST_REMOVE_HEAD(&registers, links);
+ switch(curnode->symbol->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ fprintf(ofile, "\n");
+ value = curnode->symbol->info.rinfo->address;
+ tab_str = "\t";
+ tab_str2 = "\t\t";
+ break;
+ case ALIAS:
+ {
+ symbol_t *parent;
+
+ parent = curnode->symbol->info.ainfo->parent;
+ value = parent->info.rinfo->address;
+ tab_str = "\t";
+ tab_str2 = "\t\t";
+ break;
+ }
+ case MASK:
+ case BIT:
+ value = curnode->symbol->info.minfo->mask;
+ tab_str = "\t\t";
+ tab_str2 = "\t";
+ break;
+ default:
+ value = 0; /* Quiet compiler */
+ tab_str = NULL;
+ tab_str2 = NULL;
+ stop("symtable_dump: Invalid symbol type "
+ "encountered", EX_SOFTWARE);
+ break;
+ }
+ fprintf(ofile, "#define%s%-16s%s0x%02x\n",
+ tab_str, curnode->symbol->name, tab_str2,
+ value);
+ free(curnode);
+ }
+ fprintf(ofile, "\n\n");
+
+ while (SLIST_FIRST(&constants) != NULL) {
+ symbol_node_t *curnode;
+
+ curnode = SLIST_FIRST(&constants);
+ SLIST_REMOVE_HEAD(&constants, links);
+ fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+ curnode->symbol->name,
+ curnode->symbol->info.cinfo->value);
+ free(curnode);
+ }
+
+
+ fprintf(ofile, "\n\n/* Downloaded Constant Definitions */\n");
+
+ for (i = 0; SLIST_FIRST(&download_constants) != NULL; i++) {
+ symbol_node_t *curnode;
+
+ curnode = SLIST_FIRST(&download_constants);
+ SLIST_REMOVE_HEAD(&download_constants, links);
+ fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+ curnode->symbol->name,
+ curnode->symbol->info.cinfo->value);
+ free(curnode);
+ }
+ fprintf(ofile, "#define\tDOWNLOAD_CONST_COUNT\t0x%02x\n", i);
+
+ fprintf(ofile, "\n\n/* Exported Labels */\n");
+
+ while (SLIST_FIRST(&exported_labels) != NULL) {
+ symbol_node_t *curnode;
+
+ curnode = SLIST_FIRST(&exported_labels);
+ SLIST_REMOVE_HEAD(&exported_labels, links);
+ fprintf(ofile, "#define\tLABEL_%-8s\t0x%02x\n",
+ curnode->symbol->name,
+ curnode->symbol->info.linfo->address);
+ free(curnode);
+ }
+ }
+}
+
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
new file mode 100644
index 0000000000..1100c62c2d
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
@@ -0,0 +1,202 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table definitions
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.h#11 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_symbol.h,v 1.11.2.3 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+typedef enum {
+ UNINITIALIZED,
+ REGISTER,
+ ALIAS,
+ SCBLOC,
+ SRAMLOC,
+ MASK,
+ BIT,
+ CONST,
+ DOWNLOAD_CONST,
+ LABEL,
+ CONDITIONAL,
+ MACRO
+} symtype;
+
+typedef enum {
+ RO = 0x01,
+ WO = 0x02,
+ RW = 0x03
+}amode_t;
+
+struct reg_info {
+ u_int address;
+ int size;
+ amode_t mode;
+ u_int8_t valid_bitmask;
+ u_int8_t modes;
+ int typecheck_masks;
+};
+
+typedef SLIST_HEAD(symlist, symbol_node) symlist_t;
+
+struct mask_info {
+ symlist_t symrefs;
+ u_int8_t mask;
+};
+
+struct const_info {
+ u_int value;
+ int define;
+};
+
+struct alias_info {
+ struct symbol *parent;
+};
+
+struct label_info {
+ int address;
+ int exported;
+};
+
+struct cond_info {
+ int func_num;
+};
+
+struct macro_arg {
+ STAILQ_ENTRY(macro_arg) links;
+ regex_t arg_regex;
+ char *replacement_text;
+};
+STAILQ_HEAD(macro_arg_list, macro_arg) args;
+
+struct macro_info {
+ struct macro_arg_list args;
+ int narg;
+ const char* body;
+};
+
+typedef struct expression_info {
+ symlist_t referenced_syms;
+ int value;
+} expression_t;
+
+typedef struct symbol {
+ char *name;
+ symtype type;
+ union {
+ struct reg_info *rinfo;
+ struct mask_info *minfo;
+ struct const_info *cinfo;
+ struct alias_info *ainfo;
+ struct label_info *linfo;
+ struct cond_info *condinfo;
+ struct macro_info *macroinfo;
+ }info;
+} symbol_t;
+
+typedef struct symbol_ref {
+ symbol_t *symbol;
+ int offset;
+} symbol_ref_t;
+
+typedef struct symbol_node {
+ SLIST_ENTRY(symbol_node) links;
+ symbol_t *symbol;
+} symbol_node_t;
+
+typedef struct critical_section {
+ TAILQ_ENTRY(critical_section) links;
+ int begin_addr;
+ int end_addr;
+} critical_section_t;
+
+typedef enum {
+ SCOPE_ROOT,
+ SCOPE_IF,
+ SCOPE_ELSE_IF,
+ SCOPE_ELSE
+} scope_type;
+
+typedef struct patch_info {
+ int skip_patch;
+ int skip_instr;
+} patch_info_t;
+
+typedef struct scope {
+ SLIST_ENTRY(scope) scope_stack_links;
+ TAILQ_ENTRY(scope) scope_links;
+ TAILQ_HEAD(, scope) inner_scope;
+ scope_type type;
+ int inner_scope_patches;
+ int begin_addr;
+ int end_addr;
+ patch_info_t patches[2];
+ int func_num;
+} scope_t;
+
+TAILQ_HEAD(cs_tailq, critical_section);
+SLIST_HEAD(scope_list, scope);
+TAILQ_HEAD(scope_tailq, scope);
+
+void symbol_delete __P((symbol_t *symbol));
+
+void symtable_open __P((void));
+
+void symtable_close __P((void));
+
+symbol_t *
+ symtable_get __P((char *name));
+
+symbol_node_t *
+ symlist_search __P((symlist_t *symlist, char *symname));
+
+void
+ symlist_add __P((symlist_t *symlist, symbol_t *symbol, int how));
+#define SYMLIST_INSERT_HEAD 0x00
+#define SYMLIST_SORT 0x01
+
+void symlist_free __P((symlist_t *symlist));
+
+void symlist_merge __P((symlist_t *symlist_dest, symlist_t *symlist_src1,
+ symlist_t *symlist_src2));
+void symtable_dump __P((FILE *ofile));
diff --git a/xen/drivers/scsi/aic7xxx/cam.h b/xen/drivers/scsi/aic7xxx/cam.h
new file mode 100644
index 0000000000..d7a526fe99
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/cam.h
@@ -0,0 +1,138 @@
+/*
+ * Data structures and definitions for the CAM system.
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2000 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/cam.h#11 $
+ */
+
+#ifndef _AIC7XXX_CAM_H
+#define _AIC7XXX_CAM_H 1
+
+/* Provide a mapping from CAM constructs to Linux SCSI constructs */
+
+#define CAM_BUS_WILDCARD ((u_int)~0)
+#define CAM_TARGET_WILDCARD ((u_int)~0)
+#define CAM_LUN_WILDCARD ((u_int)~0)
+
+/* CAM Status field values */
+typedef enum {
+ /* CCB request is in progress */
+ CAM_REQ_INPROG = 0x3F, /* Some value unused by Linux */
+ /* CCB request completed without error */
+ CAM_REQ_CMP = DID_OK,
+ /* CCB request aborted by the host */
+ CAM_REQ_ABORTED = DID_ABORT,
+ /* Unable to abort CCB request */
+ CAM_UA_ABORT = DID_ERROR,
+ /* CCB request completed with an error */
+ CAM_REQ_CMP_ERR = DID_ERROR,
+ /* CAM subsytem is busy */
+ CAM_BUSY = DID_BUS_BUSY,
+ /* CCB request was invalid */
+ CAM_REQ_INVALID = DID_BAD_TARGET,
+ /* Supplied Path ID is invalid */
+ CAM_PATH_INVALID = DID_BAD_TARGET,
+ /* Target Selection Timeout */
+ CAM_SEL_TIMEOUT = DID_NO_CONNECT,
+ /* Command timeout */
+ CAM_CMD_TIMEOUT = DID_ERROR, /*
+ * Should never occur in Linux
+ * as the upper level code
+ * handles all timeout processing.
+ */
+ /* SCSI error, look at error code in CCB */
+ CAM_SCSI_STATUS_ERROR = DID_OK, /* Linux looks at status byte */
+ /* SCSI Bus Reset Sent/Received */
+ CAM_SCSI_BUS_RESET = DID_RESET,
+ /* Uncorrectable parity error occurred */
+ CAM_UNCOR_PARITY = DID_PARITY,
+ /* Autosense: request sense cmd fail */
+ CAM_AUTOSENSE_FAIL = DID_ERROR,
+ /* No HBA Detected Error */
+ CAM_NO_HBA = DID_ERROR,
+ /* Data Overrun error */
+ CAM_DATA_RUN_ERR = DID_ERROR,
+ /* Unexpected Bus Free */
+ CAM_UNEXP_BUSFREE = DID_ERROR,
+ /* CCB length supplied is inadequate */
+ CAM_CCB_LEN_ERR = DID_ERROR,
+ /* Unable to provide requested capability */
+ CAM_PROVIDE_FAIL = DID_ERROR,
+ /* A SCSI BDR msg was sent to target */
+ CAM_BDR_SENT = DID_RESET,
+ /* CCB request terminated by the host */
+ CAM_REQ_TERMIO = DID_ERROR,
+ /* Unrecoverable Host Bus Adapter Error */
+ CAM_UNREC_HBA_ERROR = DID_ERROR,
+ /* The request was too large for this host */
+ CAM_REQ_TOO_BIG = DID_ERROR,
+ /*
+ * This request should be requeued to preserve
+ * transaction ordering. This typically occurs
+ * when the SIM recognizes an error that should
+ * freeze the queue and must place additional
+ * requests for the target at the sim level
+ * back into the XPT queue.
+ */
+ CAM_REQUEUE_REQ = DID_BUS_BUSY,
+
+ CAM_STATUS_MASK = 0x3F
+} cam_status;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#define SCSI_DATA_READ 1
+#define SCSI_DATA_WRITE 2
+#define SCSI_DATA_NONE 3
+#endif
+
+/*
+ * Definitions for the asynchronous callback CCB fields.
+ */
+typedef enum {
+ AC_GETDEV_CHANGED = 0x800,/* Getdev info might have changed */
+ AC_INQ_CHANGED = 0x400,/* Inquiry info might have changed */
+ AC_TRANSFER_NEG = 0x200,/* New transfer settings in effect */
+ AC_LOST_DEVICE = 0x100,/* A device went away */
+ AC_FOUND_DEVICE = 0x080,/* A new device was found */
+ AC_PATH_DEREGISTERED = 0x040,/* A path has de-registered */
+ AC_PATH_REGISTERED = 0x020,/* A new path has been registered */
+ AC_SENT_BDR = 0x010,/* A BDR message was sent to target */
+ AC_SCSI_AEN = 0x008,/* A SCSI AEN has been received */
+ AC_UNSOL_RESEL = 0x002,/* Unsolicited reselection occurred */
+ AC_BUS_RESET = 0x001 /* A SCSI bus reset occurred */
+} ac_code;
+
+typedef enum {
+ CAM_DIR_IN = SCSI_DATA_READ,
+ CAM_DIR_OUT = SCSI_DATA_WRITE,
+ CAM_DIR_NONE = SCSI_DATA_NONE
+} ccb_flags;
+
+#endif /* _AIC7XXX_CAM_H */
diff --git a/xen/drivers/scsi/aic7xxx/queue.h b/xen/drivers/scsi/aic7xxx/queue.h
new file mode 100644
index 0000000000..8adf8003a1
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/queue.h
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.38 2000/05/26 02:06:56 jake Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ CIRCLEQ
+ * _HEAD + + + + +
+ * _HEAD_INITIALIZER + + + + +
+ * _ENTRY + + + + +
+ * _INIT + + + + +
+ * _EMPTY + + + + +
+ * _FIRST + + + + +
+ * _NEXT + + + + +
+ * _PREV - - - + +
+ * _LAST - - + + +
+ * _FOREACH + + + + +
+ * _FOREACH_REVERSE - - - + +
+ * _INSERT_HEAD + + + + +
+ * _INSERT_BEFORE - + - + +
+ * _INSERT_AFTER + + + + +
+ * _INSERT_TAIL - - + + +
+ * _REMOVE_HEAD + - + - -
+ * _REMOVE + + + + +
+ *
+ */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+} while (0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_NEXT(curelm, field) = \
+ SLIST_NEXT(SLIST_NEXT(curelm, field), field); \
+ } \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ STAILQ_LAST((head)) = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (0)
+
+#define STAILQ_LAST(head) (*(head)->stqh_last)
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD(head, field); \
+ } \
+ else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ if ((STAILQ_NEXT(curelm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+ } \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
+ if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+} while (0)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_REMOVE(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+} while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+} while (0)
+
+/*
+ * Circular queue declarations.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { (void *)&(head), (void *)&(head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = CIRCLEQ_FIRST((head)); \
+ (var) != (void *)(head); \
+ (var) = CIRCLEQ_NEXT((var), field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = CIRCLEQ_LAST((head)); \
+ (var) != (void *)(head); \
+ (var) = CIRCLEQ_PREV((var), field))
+
+#define CIRCLEQ_INIT(head) do { \
+ CIRCLEQ_FIRST((head)) = (void *)(head); \
+ CIRCLEQ_LAST((head)) = (void *)(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field); \
+ CIRCLEQ_PREV((elm), field) = (listelm); \
+ if (CIRCLEQ_NEXT((listelm), field) == (void *)(head)) \
+ CIRCLEQ_LAST((head)) = (elm); \
+ else \
+ CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\
+ CIRCLEQ_NEXT((listelm), field) = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ CIRCLEQ_NEXT((elm), field) = (listelm); \
+ CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field); \
+ if (CIRCLEQ_PREV((listelm), field) == (void *)(head)) \
+ CIRCLEQ_FIRST((head)) = (elm); \
+ else \
+ CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\
+ CIRCLEQ_PREV((listelm), field) = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head)); \
+ CIRCLEQ_PREV((elm), field) = (void *)(head); \
+ if (CIRCLEQ_LAST((head)) == (void *)(head)) \
+ CIRCLEQ_LAST((head)) = (elm); \
+ else \
+ CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm); \
+ CIRCLEQ_FIRST((head)) = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ CIRCLEQ_NEXT((elm), field) = (void *)(head); \
+ CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head)); \
+ if (CIRCLEQ_FIRST((head)) == (void *)(head)) \
+ CIRCLEQ_FIRST((head)) = (elm); \
+ else \
+ CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm); \
+ CIRCLEQ_LAST((head)) = (elm); \
+} while (0)
+
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+
+#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
+
+#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if (CIRCLEQ_NEXT((elm), field) == (void *)(head)) \
+ CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field); \
+ else \
+ CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) = \
+ CIRCLEQ_PREV((elm), field); \
+ if (CIRCLEQ_PREV((elm), field) == (void *)(head)) \
+ CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field); \
+ else \
+ CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) = \
+ CIRCLEQ_NEXT((elm), field); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/scsi_iu.h b/xen/drivers/scsi/aic7xxx/scsi_iu.h
new file mode 100644
index 0000000000..801da0af4c
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/scsi_iu.h
@@ -0,0 +1,31 @@
+/*
+ * This file is in the public domain.
+ */
+#ifndef _SCSI_SCSI_IU_H
+#define _SCSI_SCSI_IU_H 1
+
+struct scsi_status_iu_header
+{
+ u_int8_t reserved[2];
+ u_int8_t flags;
+#define SIU_SNSVALID 0x2
+#define SIU_RSPVALID 0x1
+ u_int8_t status;
+ u_int8_t sense_length[4];
+ u_int8_t pkt_failures_length[4];
+ u_int8_t pkt_failures[1];
+};
+
+#define SIU_PKTFAIL_OFFSET(siu) 12
+#define SIU_PKTFAIL_CODE(siu) (scsi_4btoul((siu)->pkt_failures) & 0xFF)
+#define SIU_PFC_NONE 0
+#define SIU_PFC_CIU_FIELDS_INVALID 2
+#define SIU_PFC_TMF_NOT_SUPPORTED 4
+#define SIU_PFC_TMF_FAILED 5
+#define SIU_PFC_INVALID_TYPE_CODE 6
+#define SIU_PFC_ILLEGAL_REQUEST 7
+#define SIU_SENSE_OFFSET(siu) \
+ (12 + (((siu)->flags & SIU_RSPVALID) \
+ ? scsi_4btoul((siu)->pkt_failures_length) \
+ : 0))
+#endif /*_SCSI_SCSI_IU_H*/
diff --git a/xen/drivers/scsi/aic7xxx/scsi_message.h b/xen/drivers/scsi/aic7xxx/scsi_message.h
new file mode 100644
index 0000000000..75811e245e
--- /dev/null
+++ b/xen/drivers/scsi/aic7xxx/scsi_message.h
@@ -0,0 +1,70 @@
+/*
+ * This file is in the public domain.
+ * $FreeBSD: src/sys/cam/scsi/scsi_message.h,v 1.2 2000/05/01 20:21:29 peter Exp $
+ */
+
+/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE 0x00 /* M/M */
+#define MSG_TASK_COMPLETE 0x00 /* M/M */ /* SPI3 Terminology */
+#define MSG_EXTENDED 0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER 0x02 /* O/O */
+#define MSG_RESTOREPOINTERS 0x03 /* O/O */
+#define MSG_DISCONNECT 0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */
+#define MSG_ABORT 0x06 /* O/M */
+#define MSG_ABORT_TASK_SET 0x06 /* O/M */ /* SPI3 Terminology */
+#define MSG_MESSAGE_REJECT 0x07 /* M/M */
+#define MSG_NOOP 0x08 /* M/M */
+#define MSG_PARITY_ERROR 0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
+#define MSG_BUS_DEV_RESET 0x0c /* O/M */
+#define MSG_TARGET_RESET 0x0c /* O/M */ /* SPI3 Terminology */
+#define MSG_ABORT_TAG 0x0d /* O/O */
+#define MSG_ABORT_TASK 0x0d /* O/O */ /* SPI3 Terminology */
+#define MSG_CLEAR_QUEUE 0x0e /* O/O */
+#define MSG_CLEAR_TASK_SET 0x0e /* O/O */ /* SPI3 Terminology */
+#define MSG_INIT_RECOVERY 0x0f /* O/O */ /* Deprecated in SPI3 */
+#define MSG_REL_RECOVERY 0x10 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_TERM_IO_PROC 0x11 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_CLEAR_ACA 0x16 /* O/O */ /* SPI3 */
+#define MSG_LOGICAL_UNIT_RESET 0x17 /* O/O */ /* SPI3 */
+#define MSG_QAS_REQUEST 0x55 /* O/O */ /* SPI3 */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
+#define MSG_SIMPLE_TASK 0x20 /* O/O */ /* SPI3 Terminology */
+#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */
+#define MSG_HEAD_OF_QUEUE_TASK 0x21 /* O/O */ /* SPI3 Terminology */
+#define MSG_ORDERED_Q_TAG 0x22 /* O/O */
+#define MSG_ORDERED_TASK 0x22 /* O/O */ /* SPI3 Terminology */
+#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */
+#define MSG_ACA_TASK 0x24 /* 0/0 */ /* SPI3 */
+
+/* Identify message */ /* M/M */
+#define MSG_IDENTIFYFLAG 0x80
+#define MSG_IDENTIFY_DISCFLAG 0x40
+#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
+#define MSG_ISIDENTIFY(m) ((m) & MSG_IDENTIFYFLAG)
+#define MSG_IDENTIFY_LUNMASK 0x3F
+
+/* Extended messages (opcode and length) */
+#define MSG_EXT_SDTR 0x01
+#define MSG_EXT_SDTR_LEN 0x03
+
+#define MSG_EXT_WDTR 0x03
+#define MSG_EXT_WDTR_LEN 0x02
+#define MSG_EXT_WDTR_BUS_8_BIT 0x00
+#define MSG_EXT_WDTR_BUS_16_BIT 0x01
+#define MSG_EXT_WDTR_BUS_32_BIT 0x02 /* Deprecated in SPI3 */
+
+#define MSG_EXT_PPR 0x04 /* SPI3 */
+#define MSG_EXT_PPR_LEN 0x06
+#define MSG_EXT_PPR_PCOMP_EN 0x80
+#define MSG_EXT_PPR_RTI 0x40
+#define MSG_EXT_PPR_RD_STRM 0x20
+#define MSG_EXT_PPR_WR_FLOW 0x10
+#define MSG_EXT_PPR_HOLD_MCS 0x08
+#define MSG_EXT_PPR_QAS_REQ 0x04
+#define MSG_EXT_PPR_DT_REQ 0x02
+#define MSG_EXT_PPR_IU_REQ 0x01
diff --git a/xen/include/asm-i386/string.h b/xen/include/asm-i386/string.h
new file mode 100644
index 0000000000..385da59b5c
--- /dev/null
+++ b/xen/include/asm-i386/string.h
@@ -0,0 +1,500 @@
+#ifndef _I386_STRING_H_
+#define _I386_STRING_H_
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+/*
+ * On a 486 or Pentium, we are better off not using the
+ * byte string operations. But on a 386 or a PPro the
+ * byte string ops are faster than doing it by hand
+ * (MUCH faster on a Pentium).
+ *
+ * Also, the byte strings actually work correctly. Forget
+ * the i486 routines for now as they may be broken..
+ */
+#if FIXED_486_STRING && defined(CONFIG_X86_USE_STRING_486)
+#include <asm/string-486.h>
+#else
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. String instructions have been
+ * used through-out, making for "slightly" unclear code :-)
+ *
+ * NO Copyright (C) 1991, 1992 Linus Torvalds,
+ * consider these trivial functions to be PD.
+ */
+
+
+#define __HAVE_ARCH_STRCPY
+static inline char * strcpy(char * dest,const char *src)
+{
+int d0, d1, d2;
+__asm__ __volatile__(
+ "1:\tlodsb\n\t"
+ "stosb\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b"
+ : "=&S" (d0), "=&D" (d1), "=&a" (d2)
+ :"0" (src),"1" (dest) : "memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCPY
+static inline char * strncpy(char * dest,const char *src,size_t count)
+{
+int d0, d1, d2, d3;
+__asm__ __volatile__(
+ "1:\tdecl %2\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "stosb\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n\t"
+ "rep\n\t"
+ "stosb\n"
+ "2:"
+ : "=&S" (d0), "=&D" (d1), "=&c" (d2), "=&a" (d3)
+ :"0" (src),"1" (dest),"2" (count) : "memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRCAT
+static inline char * strcat(char * dest,const char * src)
+{
+int d0, d1, d2, d3;
+__asm__ __volatile__(
+ "repne\n\t"
+ "scasb\n\t"
+ "decl %1\n"
+ "1:\tlodsb\n\t"
+ "stosb\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b"
+ : "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
+ : "0" (src), "1" (dest), "2" (0), "3" (0xffffffff):"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCAT
+static inline char * strncat(char * dest,const char * src,size_t count)
+{
+int d0, d1, d2, d3;
+__asm__ __volatile__(
+ "repne\n\t"
+ "scasb\n\t"
+ "decl %1\n\t"
+ "movl %8,%3\n"
+ "1:\tdecl %3\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "stosb\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n"
+ "2:\txorl %2,%2\n\t"
+ "stosb"
+ : "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
+ : "0" (src),"1" (dest),"2" (0),"3" (0xffffffff), "g" (count)
+ : "memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRCMP
+static inline int strcmp(const char * cs,const char * ct)
+{
+int d0, d1;
+register int __res;
+__asm__ __volatile__(
+ "1:\tlodsb\n\t"
+ "scasb\n\t"
+ "jne 2f\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n\t"
+ "xorl %%eax,%%eax\n\t"
+ "jmp 3f\n"
+ "2:\tsbbl %%eax,%%eax\n\t"
+ "orb $1,%%al\n"
+ "3:"
+ :"=a" (__res), "=&S" (d0), "=&D" (d1)
+ :"1" (cs),"2" (ct));
+return __res;
+}
+
+#define __HAVE_ARCH_STRNCMP
+static inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+int d0, d1, d2;
+__asm__ __volatile__(
+ "1:\tdecl %3\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "scasb\n\t"
+ "jne 3f\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n"
+ "2:\txorl %%eax,%%eax\n\t"
+ "jmp 4f\n"
+ "3:\tsbbl %%eax,%%eax\n\t"
+ "orb $1,%%al\n"
+ "4:"
+ :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2)
+ :"1" (cs),"2" (ct),"3" (count));
+return __res;
+}
+
+#define __HAVE_ARCH_STRCHR
+static inline char * strchr(const char * s, int c)
+{
+int d0;
+register char * __res;
+__asm__ __volatile__(
+ "movb %%al,%%ah\n"
+ "1:\tlodsb\n\t"
+ "cmpb %%ah,%%al\n\t"
+ "je 2f\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n\t"
+ "movl $1,%1\n"
+ "2:\tmovl %1,%0\n\t"
+ "decl %0"
+ :"=a" (__res), "=&S" (d0) : "1" (s),"0" (c));
+return __res;
+}
+
+#define __HAVE_ARCH_STRRCHR
+static inline char * strrchr(const char * s, int c)
+{
+int d0, d1;
+register char * __res;
+__asm__ __volatile__(
+ "movb %%al,%%ah\n"
+ "1:\tlodsb\n\t"
+ "cmpb %%ah,%%al\n\t"
+ "jne 2f\n\t"
+ "leal -1(%%esi),%0\n"
+ "2:\ttestb %%al,%%al\n\t"
+ "jne 1b"
+ :"=g" (__res), "=&S" (d0), "=&a" (d1) :"0" (0),"1" (s),"2" (c));
+return __res;
+}
+
+#define __HAVE_ARCH_STRLEN
+static inline size_t strlen(const char * s)
+{
+int d0;
+register int __res;
+__asm__ __volatile__(
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %0\n\t"
+ "decl %0"
+ :"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffff));
+return __res;
+}
+
+static inline void * __memcpy(void * to, const void * from, size_t n)
+{
+int d0, d1, d2;
+__asm__ __volatile__(
+ "rep ; movsl\n\t"
+ "testb $2,%b4\n\t"
+ "je 1f\n\t"
+ "movsw\n"
+ "1:\ttestb $1,%b4\n\t"
+ "je 2f\n\t"
+ "movsb\n"
+ "2:"
+ : "=&c" (d0), "=&D" (d1), "=&S" (d2)
+ :"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
+ : "memory");
+return (to);
+}
+
+/*
+ * This looks horribly ugly, but the compiler can optimize it totally,
+ * as the count is constant.
+ */
+static inline void * __constant_memcpy(void * to, const void * from, size_t n)
+{
+ switch (n) {
+ case 0:
+ return to;
+ case 1:
+ *(unsigned char *)to = *(const unsigned char *)from;
+ return to;
+ case 2:
+ *(unsigned short *)to = *(const unsigned short *)from;
+ return to;
+ case 3:
+ *(unsigned short *)to = *(const unsigned short *)from;
+ *(2+(unsigned char *)to) = *(2+(const unsigned char *)from);
+ return to;
+ case 4:
+ *(unsigned long *)to = *(const unsigned long *)from;
+ return to;
+ case 6: /* for Ethernet addresses */
+ *(unsigned long *)to = *(const unsigned long *)from;
+ *(2+(unsigned short *)to) = *(2+(const unsigned short *)from);
+ return to;
+ case 8:
+ *(unsigned long *)to = *(const unsigned long *)from;
+ *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+ return to;
+ case 12:
+ *(unsigned long *)to = *(const unsigned long *)from;
+ *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+ *(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+ return to;
+ case 16:
+ *(unsigned long *)to = *(const unsigned long *)from;
+ *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+ *(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+ *(3+(unsigned long *)to) = *(3+(const unsigned long *)from);
+ return to;
+ case 20:
+ *(unsigned long *)to = *(const unsigned long *)from;
+ *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+ *(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+ *(3+(unsigned long *)to) = *(3+(const unsigned long *)from);
+ *(4+(unsigned long *)to) = *(4+(const unsigned long *)from);
+ return to;
+ }
+#define COMMON(x) \
+__asm__ __volatile__( \
+ "rep ; movsl" \
+ x \
+ : "=&c" (d0), "=&D" (d1), "=&S" (d2) \
+ : "0" (n/4),"1" ((long) to),"2" ((long) from) \
+ : "memory");
+{
+ int d0, d1, d2;
+ switch (n % 4) {
+ case 0: COMMON(""); return to;
+ case 1: COMMON("\n\tmovsb"); return to;
+ case 2: COMMON("\n\tmovsw"); return to;
+ default: COMMON("\n\tmovsw\n\tmovsb"); return to;
+ }
+}
+
+#undef COMMON
+}
+
+#define __HAVE_ARCH_MEMCPY
+
+#define memcpy(t, f, n) \
+(__builtin_constant_p(n) ? \
+ __constant_memcpy((t),(f),(n)) : \
+ __memcpy((t),(f),(n)))
+
+
+/*
+ * struct_cpy(x,y), copy structure *x into (matching structure) *y.
+ *
+ * We get link-time errors if the structure sizes do not match.
+ * There is no runtime overhead, it's all optimized away at
+ * compile time.
+ */
+//extern void __struct_cpy_bug (void);
+
+/*
+#define struct_cpy(x,y) \
+({ \
+ if (sizeof(*(x)) != sizeof(*(y))) \
+ __struct_cpy_bug; \
+ memcpy(x, y, sizeof(*(x))); \
+})
+*/
+
+#define __HAVE_ARCH_MEMMOVE
+static inline void * memmove(void * dest,const void * src, size_t n)
+{
+int d0, d1, d2;
+if (dest<src)
+__asm__ __volatile__(
+ "rep\n\t"
+ "movsb"
+ : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+ :"0" (n),"1" (src),"2" (dest)
+ : "memory");
+else
+__asm__ __volatile__(
+ "std\n\t"
+ "rep\n\t"
+ "movsb\n\t"
+ "cld"
+ : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+ :"0" (n),
+ "1" (n-1+(const char *)src),
+ "2" (n-1+(char *)dest)
+ :"memory");
+return dest;
+}
+
+#define memcmp __builtin_memcmp
+
+#define __HAVE_ARCH_MEMCHR
+static inline void * memchr(const void * cs,int c,size_t count)
+{
+int d0;
+register void * __res;
+if (!count)
+ return NULL;
+__asm__ __volatile__(
+ "repne\n\t"
+ "scasb\n\t"
+ "je 1f\n\t"
+ "movl $1,%0\n"
+ "1:\tdecl %0"
+ :"=D" (__res), "=&c" (d0) : "a" (c),"0" (cs),"1" (count));
+return __res;
+}
+
+static inline void * __memset_generic(void * s, char c,size_t count)
+{
+int d0, d1;
+__asm__ __volatile__(
+ "rep\n\t"
+ "stosb"
+ : "=&c" (d0), "=&D" (d1)
+ :"a" (c),"1" (s),"0" (count)
+ :"memory");
+return s;
+}
+
+/* we might want to write optimized versions of these later */
+#define __constant_count_memset(s,c,count) __memset_generic((s),(c),(count))
+
+/*
+ * memset(x,0,y) is a reasonably common thing to do, so we want to fill
+ * things 32 bits at a time even when we don't know the size of the
+ * area at compile-time..
+ */
+static inline void * __constant_c_memset(void * s, unsigned long c, size_t count)
+{
+int d0, d1;
+__asm__ __volatile__(
+ "rep ; stosl\n\t"
+ "testb $2,%b3\n\t"
+ "je 1f\n\t"
+ "stosw\n"
+ "1:\ttestb $1,%b3\n\t"
+ "je 2f\n\t"
+ "stosb\n"
+ "2:"
+ : "=&c" (d0), "=&D" (d1)
+ :"a" (c), "q" (count), "0" (count/4), "1" ((long) s)
+ :"memory");
+return (s);
+}
+
+/* Added by Gertjan van Wingerde to make minix and sysv module work */
+#define __HAVE_ARCH_STRNLEN
+static inline size_t strnlen(const char * s, size_t count)
+{
+int d0;
+register int __res;
+__asm__ __volatile__(
+ "movl %2,%0\n\t"
+ "jmp 2f\n"
+ "1:\tcmpb $0,(%0)\n\t"
+ "je 3f\n\t"
+ "incl %0\n"
+ "2:\tdecl %1\n\t"
+ "cmpl $-1,%1\n\t"
+ "jne 1b\n"
+ "3:\tsubl %2,%0"
+ :"=a" (__res), "=&d" (d0)
+ :"c" (s),"1" (count));
+return __res;
+}
+/* end of additional stuff */
+
+//#define __HAVE_ARCH_STRSTR
+
+//extern char *strstr(const char *cs, const char *ct);
+
+/*
+ * This looks horribly ugly, but the compiler can optimize it totally,
+ * as we by now know that both pattern and count is constant..
+ */
+static inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count)
+{
+ switch (count) {
+ case 0:
+ return s;
+ case 1:
+ *(unsigned char *)s = pattern;
+ return s;
+ case 2:
+ *(unsigned short *)s = pattern;
+ return s;
+ case 3:
+ *(unsigned short *)s = pattern;
+ *(2+(unsigned char *)s) = pattern;
+ return s;
+ case 4:
+ *(unsigned long *)s = pattern;
+ return s;
+ }
+#define COMMON(x) \
+__asm__ __volatile__( \
+ "rep ; stosl" \
+ x \
+ : "=&c" (d0), "=&D" (d1) \
+ : "a" (pattern),"0" (count/4),"1" ((long) s) \
+ : "memory")
+{
+ int d0, d1;
+ switch (count % 4) {
+ case 0: COMMON(""); return s;
+ case 1: COMMON("\n\tstosb"); return s;
+ case 2: COMMON("\n\tstosw"); return s;
+ default: COMMON("\n\tstosw\n\tstosb"); return s;
+ }
+}
+
+#undef COMMON
+}
+
+#define __constant_c_x_memset(s, c, count) \
+(__builtin_constant_p(count) ? \
+ __constant_c_and_count_memset((s),(c),(count)) : \
+ __constant_c_memset((s),(c),(count)))
+
+#define __memset(s, c, count) \
+(__builtin_constant_p(count) ? \
+ __constant_count_memset((s),(c),(count)) : \
+ __memset_generic((s),(c),(count)))
+
+#define __HAVE_ARCH_MEMSET
+#define memset(s, c, count) \
+(__builtin_constant_p(c) ? \
+ __constant_c_x_memset((s),(0x01010101UL*(unsigned char)(c)),(count)) : \
+ __memset((s),(c),(count)))
+
+/*
+ * find the first occurrence of byte 'c', or 1 past the area if none
+ */
+#define __HAVE_ARCH_MEMSCAN
+static inline void * memscan(void * addr, int c, size_t size)
+{
+ if (!size)
+ return addr;
+ __asm__("repnz; scasb\n\t"
+ "jnz 1f\n\t"
+ "dec %%edi\n"
+ "1:"
+ : "=D" (addr), "=c" (size)
+ : "0" (addr), "1" (size), "a" (c));
+ return addr;
+}
+
+#endif /* CONFIG_X86_USE_STRING_486 */
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/xen/include/xeno/lib.h b/xen/include/xeno/lib.h
index cd40d119f0..a9eeda6716 100644
--- a/xen/include/xeno/lib.h
+++ b/xen/include/xeno/lib.h
@@ -14,6 +14,9 @@
printk("Memory Reservation 0x%lx, %lu bytes\n", (_p), (_l))
/* lib.c */
+#include <xeno/string.h>
+
+/* JWS - pulled over linux string library ({asm,linux}/string.h)
int memcmp(const void * cs,const void * ct,size_t count);
void * memcpy(void * dest,const void *src,size_t count);
int strncmp(const char * cs,const char * ct,size_t count);
@@ -25,6 +28,8 @@ size_t strnlen(const char * s, size_t count);
size_t strlen(const char * s);
char * strchr(const char *,int);
char * strstr(const char * s1,const char * s2);
+*/
+
unsigned long str_to_quad(unsigned char *s);
unsigned char *quad_to_str(unsigned long q, unsigned char *s);
@@ -46,6 +51,7 @@ extern int sscanf(const char *, const char *, ...)
__attribute__ ((format (scanf,2,3)));
extern int vsscanf(const char *, const char *, va_list);
long simple_strtol(const char *cp,char **endp,unsigned int base);
+unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base);
long long simple_strtoll(const char *cp,char **endp,unsigned int base);
#endif /* __LIB_H__ */
diff --git a/xen/include/xeno/string.h b/xen/include/xeno/string.h
new file mode 100644
index 0000000000..71ab1c4ef8
--- /dev/null
+++ b/xen/include/xeno/string.h
@@ -0,0 +1,90 @@
+#ifndef _LINUX_STRING_H_
+#define _LINUX_STRING_H_
+
+/* We don't want strings.h stuff being user by user stuff by accident */
+
+#ifdef __KERNEL__
+
+#include <linux/types.h> /* for size_t */
+//#include <linux/stddef.h> /* for NULL */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __kernel_size_t size_t
+
+extern char * ___strtok;
+extern char * strpbrk(const char *,const char *);
+extern char * strtok(char *,const char *);
+extern char * strsep(char **,const char *);
+extern __kernel_size_t strspn(const char *,const char *);
+
+
+/*
+ * Include machine specific inline routines
+ */
+#include <asm/string.h>
+
+#ifndef __HAVE_ARCH_STRCPY
+extern char * strcpy(char *,const char *);
+#endif
+#ifndef __HAVE_ARCH_STRNCPY
+extern char * strncpy(char *,const char *, __kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_STRCAT
+extern char * strcat(char *, const char *);
+#endif
+#ifndef __HAVE_ARCH_STRNCAT
+extern char * strncat(char *, const char *, __kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_STRCMP
+extern int strcmp(const char *,const char *);
+#endif
+#ifndef __HAVE_ARCH_STRNCMP
+extern int strncmp(const char *,const char *,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_STRNICMP
+extern int strnicmp(const char *, const char *, __kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_STRCHR
+extern char * strchr(const char *,int);
+#endif
+#ifndef __HAVE_ARCH_STRRCHR
+extern char * strrchr(const char *,int);
+#endif
+#ifndef __HAVE_ARCH_STRSTR
+extern char * strstr(const char *,const char *);
+#endif
+#ifndef __HAVE_ARCH_STRLEN
+extern __kernel_size_t strlen(const char *);
+#endif
+#ifndef __HAVE_ARCH_STRNLEN
+extern __kernel_size_t strnlen(const char *,__kernel_size_t);
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET
+extern void * memset(void *,int,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMCPY
+extern void * memcpy(void *,const void *,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMMOVE
+extern void * memmove(void *,const void *,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMSCAN
+extern void * memscan(void *,int,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMCMP
+extern int memcmp(const void *,const void *,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMCHR
+extern void * memchr(const void *,int,__kernel_size_t);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+#endif /* _LINUX_STRING_H_ */