summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.transmeta.com>2002-12-23 04:31:37 -0800
committerLinus Torvalds <torvalds@home.transmeta.com>2002-12-23 04:31:37 -0800
commitc4e3c771b5bd9f0ed5341a2743e1a0ac6bf4d6fa (patch)
tree938b09002fccc7b571e72abb17f016491b47a8d0
parenta27aaaf7fba7843686d66af56d9b0b0294f44d4b (diff)
parent91076e502fb8684d1c7269a72edc84dd651f25e8 (diff)
Merge bk://linux-scsi.bkbits.net/scsi-for-linus-2.5
into home.transmeta.com:/home/torvalds/v2.5/linux
-rw-r--r--Documentation/scsi/aic79xx.txt323
-rw-r--r--Documentation/scsi/aic7xxx.txt771
-rw-r--r--drivers/scsi/53c700.c138
-rw-r--r--drivers/scsi/53c700.h33
-rw-r--r--drivers/scsi/Kconfig45
-rw-r--r--drivers/scsi/NCR_D700.c1
-rw-r--r--drivers/scsi/aic7xxx/Kconfig53
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic79xx96
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic7xxx100
-rw-r--r--drivers/scsi/aic7xxx/Makefile78
-rw-r--r--drivers/scsi/aic7xxx/aic7770.c129
-rw-r--r--drivers/scsi/aic7xxx/aic7770_osm.c (renamed from drivers/scsi/aic7xxx/aic7770_linux.c)25
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.h1480
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.reg3933
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.seq1868
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c9383
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_inline.h951
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c5242
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.h1281
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm_pci.c431
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_pci.c912
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_proc.c345
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg.h_shipped3776
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped3630
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_seq.h_shipped1067
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.h121
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.reg661
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.seq183
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_93cx6.c163
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_93cx6.h12
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_core.c939
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_inline.h101
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_linux.c2827
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_linux_host.h87
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c5136
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.h549
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm_pci.c (renamed from drivers/scsi/aic7xxx/aic7xxx_linux_pci.c)87
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_pci.c482
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_proc.c181
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped1137
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped1689
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped1175
-rw-r--r--drivers/scsi/aic7xxx/aicasm/Makefile36
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm.c144
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm.h13
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_gram.y653
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h4
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y164
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l155
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_scan.l295
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c507
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h80
-rw-r--r--drivers/scsi/aic7xxx/aiclib.c1226
-rw-r--r--drivers/scsi/aic7xxx/aiclib.h992
-rw-r--r--drivers/scsi/aic7xxx/cam.h85
-rw-r--r--drivers/scsi/aic7xxx/scsi_iu.h31
-rw-r--r--drivers/scsi/aic7xxx/scsi_message.h11
-rw-r--r--drivers/scsi/hosts.h3
-rw-r--r--drivers/scsi/ide-scsi.c225
-rw-r--r--drivers/scsi/lasi700.c11
-rw-r--r--drivers/scsi/lasi700.h6
-rw-r--r--drivers/scsi/scsi.c43
-rw-r--r--drivers/scsi/scsi.h1
-rw-r--r--drivers/scsi/scsi_debug.c180
-rw-r--r--drivers/scsi/scsi_scan.c113
-rw-r--r--drivers/scsi/scsi_syms.c5
-rw-r--r--drivers/scsi/st.c16
67 files changed, 50427 insertions, 6193 deletions
diff --git a/Documentation/scsi/aic79xx.txt b/Documentation/scsi/aic79xx.txt
new file mode 100644
index 000000000000..3f92f1de0ccc
--- /dev/null
+++ b/Documentation/scsi/aic79xx.txt
@@ -0,0 +1,323 @@
+====================================================================
+= Adaptec Ultra320 Family Manager Set v1.1.1 =
+= =
+= README for =
+= The Linux Operating System =
+====================================================================
+
+The following information is available in this file:
+
+ 1. Supported Hardware
+ 2. Version History
+ 3. Command Line Options
+ 4. Additional Notes
+ 5. Contacting Adaptec
+
+
+1. Supported Hardware
+
+ The following Adaptec SCSI Host Adapters are supported by this
+ driver set.
+
+ Ultra320 Adapters Description
+ ----------------------------------------------------------------
+ Adaptec SCSI Card 39320 Dual Channel 64-bit PCI-X 133MHz to
+ Ultra320 SCSI Card (one external
+ 68-pin, two internal 68-pin)
+ Adaptec SCSI Card 39320D Dual Channel 64-bit PCI-X 133MHz to
+ Ultra320 SCSI Card (two external VHDC
+ and one internal 68-pin)
+ Adaptec SCSI Card 29320 Single Channel 64-bit PCI-X 133MHz to
+ Ultra320 SCSI Card (one external
+ 68-pin, two internal 68-pin, one
+ internal 50-pin)
+ Adaptec SCSI Card 29320LP Single Channel 64-bit Low Profile
+ PCI-X 133MHz to Ultra320 SCSI Card
+ (One external VHDC, one internal
+ 68-pin)
+ AIC-7901A Single Channel 64-bit PCI-X 133MHz to
+ Ultra320 SCSI ASIC
+ AIC-7902A4 Dual Channel 64-bit PCI-X 133MHz to
+ Ultra320 SCSI ASIC
+
+
+2. Version History
+
+ (V1.1.1, September 2002) Added support for the Linux 2.5.X kernel series
+
+ (V1.1, August 2002) Added support for four additional SCSI
+ products: ASC-39320, ASC-29320, ASC-29320LP, AIC-7901.
+
+ (V1.0, May 2002) This is the initial release of the
+ Ultra320 FMS. The following is a list of supported features:
+
+ 2.1. Software/Hardware Features
+ - Support for the SPI-4 "Ultra320" standard:
+ - 320MB/s transfer rates
+ - Packetized SCSI Protocol at 160MB/s and 320MB/s
+ - Quick Arbitration Selection (QAS)
+ - Initiator Mode (target mode not currently
+ supported)
+ - Support for the PCI-x standard up to 133MHz
+ - Support for the PCI v2.2 standard
+
+ 2.2. Operating System Support:
+ - Redhat Linux 7.2, 7.3, Advanced Server 2.1
+ - SuSE Linux 7.3, 8.0, Enterprise Server 7
+ - only Intel and AMD x86 supported at this time
+ - >4GB memory configurations supported.
+
+ Refer to the User's Guide for more details on this.
+
+
+3. Command Line Options
+
+ WARNING: ALTERING OR ADDING THESE DRIVER PARAMETERS
+ INCORRECTLY CAN RENDER YOUR SYSTEM INOPERABLE.
+ USE THEM WITH CAUTION.
+
+ Edit the file "modules.conf" in the directory /etc and add/edit a
+ line containing 'options aic79xx=[command[,command...]]' where
+ 'command' is one or more of the following:
+ -----------------------------------------------------------------
+ Option: verbose
+ Definition: enable additional informative messages during
+ driver operation.
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: debug:[value]
+ Definition: Enables various levels of debugging information
+ Possible Values: 0x0000 = no debugging, 0xffff = full debugging
+ Default Value: 0x0000
+ -----------------------------------------------------------------
+ Option: no_reset
+ Definition: Do not reset the bus during the initial probe
+ phase
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: extended
+ Definition: Force extended translation on the controller
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: periodic_otag
+ Definition: Send an ordered tag periodically to prevent
+ tag starvation. Needed for some older devices
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: reverse_scan
+ Definition: Probe the scsi bus in reverse order, starting
+ with target 15
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: global_tag_depth
+ Definition: Global tag depth for all targets on all busses.
+ This option sets the default tag depth which
+ may be selectively overridden vi the tag_info
+ option.
+ Possible Values: 1 - 253
+ Default Value: 32
+ -----------------------------------------------------------------
+ Option: tag_info:{{value[,value...]}[,{value[,value...]}...]}
+ Definition: Set the per-target tagged queue depth on a
+ per controller basis. Both controllers and targets
+ may be ommitted indicating that they should retain
+ the default tag depth.
+ Examples: tag_info:{{16,32,32,64,8,8,,32,32,32,32,32,32,32,32,32}
+ On Controller 0
+ specifies a tag depth of 16 for target 0
+ specifies a tag depth of 64 for target 3
+ specifies a tag depth of 8 for targets 4 and 5
+ leaves target 6 at the default
+ specifies a tag depth of 32 for targets 1,2,7-15
+ All other targets retain the default depth.
+
+ tag_info:{{},{32,,32}}
+ On Controller 1
+ specifies a tag depth of 32 for targets 0 and 2
+ All other targets retain the default depth.
+
+ Possible Values: 1 - 253
+ Default Value: 32
+ -----------------------------------------------------------------
+ Option: rd_strm: {rd_strm_bitmask[,rd_strm_bitmask...]}
+ Definition: Enable read streaming on a per target basis.
+ The rd_strm_bitmask is a 16 bit hex value in which
+ each bit represents a target. Setting the target's
+ bit to '1' enables read streaming for that
+ target. Controllers may be ommitted indicating that
+ they should retain the default read streaming setting.
+ Example: rd_strm:{0x0041}
+ On Controller 0
+ enables read streaming for targets 0 and 6.
+ disables read streaming for targets 1-5,7-15.
+ All other targets retain the default read
+ streaming setting.
+ Example: rd_strm:{0x0023,,0xFFFF}
+ On Controller 0
+ enables read streaming for targets 1,2, and 5.
+ disables read streaming for targets 3,4,6-15.
+ On Controller 2
+ enables read streaming for all targets.
+ All other targets retain the default read
+ streaming setting.
+
+ Possible Values: 0x0000 - 0xffff
+ Default Value: 0x0000
+ -----------------------------------------------------------------
+ Option: precomp: {value[,value...]}
+ Definition: Set IO Cell precompensation value on a per-controller
+ basis.
+ Controllers may be ommitted indicating that
+ they should retain the default precompensation setting.
+ Example: precomp:{0x1}
+ On Controller 0 set precompensation to 1.
+ Example: precomp:{1,,7}
+ On Controller 0 set precompensation to 1.
+ On Controller 2 set precompensation to 8.
+
+ Possible Values: 0 - 7
+ Default Value: Varies based on chip revision
+ -----------------------------------------------------------------
+ Option: slewrate: {value[,value...]}
+ Definition: Set IO Cell slew rate on a per-controller basis.
+ Controllers may be ommitted indicating that
+ they should retain the default slew rate setting.
+ Example: slewrate:{0x1}
+ On Controller 0 set slew rate to 1.
+ Example: slewrate :{1,,8}
+ On Controller 0 set slew rate to 1.
+ On Controller 2 set slew rate to 8.
+
+ Possible Values: 0 - 15
+ Default Value: Varies based on chip revision
+ -----------------------------------------------------------------
+ Option: amplitude: {value[,value...]}
+ Definition: Set IO Cell signal amplitude on a per-controller basis.
+ Controllers may be ommitted indicating that
+ they should retain the default read streaming setting.
+ Example: amplitude:{0x1}
+ On Controller 0 set amplitude to 1.
+ Example: amplitude :{1,,7}
+ On Controller 0 set amplitude to 1.
+ On Controller 2 set amplitude to 7.
+
+ Possible Values: 1 - 7
+ Default Value: Varies based on chip revision
+ -----------------------------------------------------------------
+ Option: seltime:[value]
+ Definition: Specifies the selection timeout value
+ Possible Values: 0 = 256ms, 1 = 128ms, 2 = 64ms, 3 = 32ms
+ Default Value: 0
+ -----------------------------------------------------------------
+
+ Example: 'options aic79xx=verbose,rd_strm:{{0x0041}}'
+ enables verbose output in the driver and turns read streaming on
+ for targets 0 and 6 of Controller 0.
+
+4. Additional Notes
+
+ 4.1. Known/Unresolved or FYI Issues
+
+ * Domain Validation is not implemented.
+ * Under SuSE Linux Enterprise 7, the driver may fail to operate
+ correctly due to a problem with PCI interrupt routing in the
+ Linux kernel. Please contact SuSE for an updated Linux
+ kernel.
+
+ 4.2. Third-Party Compatibility Issues
+
+ * Adaptec only supports Ultra320 hard drives running
+ the latest firmware available. Please check with
+ your hard drive manufacturer to ensure you have the
+ latest version.
+
+ 4.3. Operating System or Technology Limitations
+
+ * PCI Hot Plug is untested and may cause the operating system
+ to stop responding.
+
+
+5. Contacting Adaptec
+
+ A Technical Support Identification (TSID) Number is required for
+ Adaptec technical support.
+ - The 12-digit TSID can be found on the white barcode-type label
+ included inside the box with your product. The TSID helps us
+ provide more efficient service by accurately identifying your
+ product and support status.
+ Support Options
+ - Search the Adaptec Support Knowledgebase (ASK) at
+ http://ask.adaptec.com for articles, troubleshooting tips, and
+ frequently asked questions for your product.
+ - For support via Email, submit your question to Adaptec's
+ Technical Support Specialists at http://ask.adaptec.com.
+
+ North America
+ - Visit our Web site at http://www.adaptec.com.
+ - To speak with a Fibre Channel/RAID/External Storage Technical
+ Support Specialist, call 1-321-207-2000,
+ Hours: Monday-Friday, 3:00 A.M. to 5:00 P.M., PST.
+ (Not open on holidays)
+ - For Technical Support in all other technologies including
+ SCSI, call 1-408-934-7274,
+ Hours: Monday-Friday, 6:00 A.M. to 5:00 P.M., PST.
+ (Not open on holidays)
+ - For after hours support, call 1-800-416-8066 ($99/call,
+ $149/call on holidays)
+ - To order Adaptec products including software and cables, call
+ 1-800-442-7274 or 1-408-957-7274. You can also visit our
+ online store at http://www.adaptecstore.com
+
+ Europe
+ - Visit our Web site at http://www.adaptec-europe.com.
+ - English and French: To speak with a Technical Support
+ Specialist, call one of the following numbers:
+ - English: +32-2-352-3470
+ - French: +32-2-352-3460
+ Hours: Monday-Thursday, 10:00 to 12:30, 13:30 to 17:30 CET
+ Friday, 10:00 to 12:30, 13:30 to 16:30 CET
+ - German: To speak with a Technical Support Specialist,
+ call +49-89-456-40660
+ Hours: Monday-Thursday, 09:30 to 12:30, 13:30 to 16:30 CET
+ Friday, 09:30 to 12:30, 13:30 to 15:00 CET
+ - To order Adaptec products, including accessories and cables:
+ - UK: +0800-96-65-26 or fax +0800-731-02-95
+ - Other European countries: +32-11-300-379
+
+ Australia and New Zealand
+ - Visit our Web site at http://www.adaptec.com.au.
+ - To speak with a Technical Support Specialist, call
+ +612-9416-0698
+ Hours: Monday-Friday, 10:00 A.M. to 4:30 P.M., EAT
+ (Not open on holidays)
+
+ Japan
+ - To speak with a Technical Support Specialist, call
+ +81-3-5308-6120
+ Hours: Monday-Friday, 9:00 a.m. to 12:00 p.m., 1:00 p.m. to
+ 6:00 p.m. TSC
+
+ Hong Kong and China
+ - To speak with a Technical Support Specialist, call
+ +852-2869-7200
+ Hours: Monday-Friday, 10:00 to 17:00.
+ - Fax Technical Support at +852-2869-7100.
+
+ Singapore
+ - To speak with a Technical Support Specialist, call
+ +65-245-7470
+ Hours: Monday-Friday, 10:00 to 17:00.
+ - Fax Technical Support at +852-2869-7100
+
+-------------------------------------------------------------------
+
+(c) 2002 Adaptec, Inc. All Rights Reserved. No part of this
+publication may be reproduced, stored in a retrieval system, or
+transmitted in any form or by any means, electronic, mechanical,
+photocopying, recording or otherwise, without prior written consent
+of Adaptec, Inc., 691 South Milpitas Blvd., Milpitas, CA 95035.
diff --git a/Documentation/scsi/aic7xxx.txt b/Documentation/scsi/aic7xxx.txt
index 9088d7736074..2246ca8978eb 100644
--- a/Documentation/scsi/aic7xxx.txt
+++ b/Documentation/scsi/aic7xxx.txt
@@ -1,477 +1,294 @@
- AIC7xxx Driver for Linux
-
-Introduction
-----------------------------
-The AIC7xxx SCSI driver adds support for Adaptec (http://www.adaptec.com)
-SCSI controllers and chipsets. Major portions of the driver and driver
-development are shared between both Linux and FreeBSD. Support for the
-AIC-7xxx chipsets have been in the default Linux kernel since approximately
-linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
-2.1.0 or later.
-
- Supported cards/chipsets
- ----------------------------
- Adaptec Cards
- ----------------------------
- AHA-274x
- AHA-274xT
- AHA-274xW
- AHA-284x
- AHA-284xW
- All PCI based cards using any of the chipsets listed under motherboard
- chipsets. In general, this means *all* of the Adaptec SCSI controllers
- except the ones specifically excluded later on in this document.
-
- Motherboard Chipsets
- ----------------------------
- AIC-777x
- AIC-785x
- AIC-786x
- AIC-787x
- AIC-788x
- AIC-789x
- AIC-3860
-
- Bus Types
- ----------------------------
- W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support
- SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s.
- U - Ultra SCSI, transfer rates up to 40MB/s.
- U2- Ultra 2 SCSI, transfer rates up to 80MB/s.
- U3- Ultra 3 SCSI, transfer rates up to 160MB/s.
- D - Differential SCSI.
- T - Twin Channel SCSI. Up to 14 SCSI devices.
-
- AHA-274x - EISA SCSI controller
- AHA-284x - VLB SCSI controller
- AHA-29xx - PCI SCSI controller
- AHA-39xx - PCI controllers with multiple separate SCSI channels on-board.
-
- Not Supported Devices
- ------------------------------
- Adaptec Cards
- ----------------------------
- AHA-2920 (Only the cards that use the Future Domain chipset are not
- supported, any 2920 cards based on Adaptec AIC chipsets,
- such as the 2920C, are supported)
- AAA-13x Raid Adapters
- AAA-113x Raid Port Card
-
- Motherboard Chipsets
- ----------------------------
- AIC-781x
-
- Bus Types
- ----------------------------
- R - Raid Port busses are not supported.
-
- The hardware RAID devices sold by Adaptec are *NOT* supported by this
- driver (and will people please stop emailing me about them, they are
- a totally separate beast from the bare SCSI controllers and this driver
- can not be retrofitted in any sane manner to support the hardware RAID
- features on those cards - Doug Ledford).
-
-
- People
- ------------------------------
- Justin T Gibbs gibbs@plutotech.com
- (BSD Driver Author)
- Dan Eischen deischen@iworks.InterWorks.org
- (Original Linux Driver Co-maintainer)
- Dean Gehnert deang@teleport.com
- (Original Linux FTP/patch maintainer)
- Jess Johnson jester@frenzy.com
- (AIC7xxx FAQ author)
- Doug Ledford dledford@redhat.com
- (Current Linux aic7xxx-5.x.x Driver/Patch/FTP maintainer)
-
- Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
- author of the driver. John has since retired from the project. Thanks
- again for all his work!
-
- Mailing list
- ------------------------------
- There is a mailing list available for users who want to track development
- and converse with other users and developers. This list is for both
- FreeBSD and Linux support of the AIC7xxx chipsets.
-
- To subscribe to the AIC7xxx mailing list send mail to the list server,
- with "subscribe AIC7xxx" in the body (no Subject: required):
- To: majordomo@FreeBSD.ORG
- ---
- subscribe AIC7xxx
-
- To unsubscribe from the list, send mail to the list server with:
- To: majordomo@FreeBSD.ORG
- ---
- unsubscribe AIC7xxx
-
- Send regular messages and replies to: AIC7xxx@FreeBSD.ORG
-
- Boot Command line options
- ------------------------------
- "aic7xxx=no_reset" - Eliminate the SCSI bus reset during startup.
- Some SCSI devices need the initial reset that this option disables
- in order to work. If you have problems at bootup, please make sure
- you aren't using this option.
-
- "aic7xxx=reverse_scan" - Certain PCI motherboards scan for devices at
- bootup by scanning from the highest numbered PCI device to the
- lowest numbered PCI device, others do just the opposite and scan
- from lowest to highest numbered PCI device. There is no reliable
- way to autodetect this ordering. So, we default to the most common
- order, which is lowest to highest. Then, in case your motherboard
- scans from highest to lowest, we have this option. If your BIOS
- finds the drives on controller A before controller B but the linux
- kernel finds your drives on controller B before A, then you should
- use this option.
-
- "aic7xxx=extended" - Force the driver to detect extended drive translation
- on your controller. This helps those people who have cards without
- a SEEPROM make sure that linux and all other operating systems think
- the same way about your hard drives.
-
- "aic7xxx=scbram" - Some cards have external SCB RAM that can be used to
- give the card more hardware SCB slots. This allows the driver to use
- that SCB RAM. Without this option, the driver won't touch the SCB
- RAM because it is known to cause problems on a few cards out there
- (such as 3985 class cards).
-
- "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel
- to use the correct IRQ type for your card. This only applies to EISA
- based controllers. On these controllers, 0 is for Edge triggered
- interrupts, and 1 is for Level triggered interrupts. If you aren't
- sure or don't know which IRQ trigger type your EISA card uses, then
- let the kernel autodetect the trigger type.
-
- "aic7xxx=verbose" - This option can be used in one of two ways. If you
- simply specify aic7xxx=verbose, then the kernel will automatically
- pick the default set of verbose messages for you to see.
- Alternatively, you can specify the command as
- "aic7xxx=verbose:0xXXXX" where the X entries are replaced with
- hexadecimal digits. This option is a bit field type option. For
- a full listing of the available options, search for the
- #define VERBOSE_xxxxxx lines in the aic7xxx.c file. If you want
- verbose messages, then it is recommended that you simply use the
- aic7xxx=verbose variant of this command.
-
- "aic7xxx=pci_parity:x" - This option controls whether or not the driver
- enables PCI parity error checking on the PCI bus. By default, this
- checking is disabled. To enable the checks, simply specify pci_parity
- with no value afterwords. To reverse the parity from even to odd,
- supply any number other than 0 or 255. In short:
- pci_parity - Even parity checking (even is the normal PCI parity)
- pci_parity:x - Where x > 0, Odd parity checking
- pci_parity:0 - No check (default)
- NOTE: In order to get Even PCI parity checking, you must use the
- version of the option that does not include the : and a number at
- the end (unless you want to enter exactly 2^32 - 1 as the number).
-
- "aic7xxx=no_probe" - This option will disable the probing for any VLB
- based 2842 controllers and any EISA based controllers. This is
- needed on certain newer motherboards where the normal EISA I/O ranges
- have been claimed by other PCI devices. Probing on those machines
- will often result in the machine crashing or spontaneously rebooting
- during startup. Examples of machines that need this are the
- Dell PowerEdge 6300 machines.
-
- "aic7xxx=seltime:2" - This option controls how long the card waits
- during a device selection sequence for the device to respond.
- The original SCSI spec says that this "should be" 256ms. This
- is generally not required with modern devices. However, some
- very old SCSI I devices need the full 256ms. Most modern devices
- can run fine with only 64ms. The default for this option is
- 64ms. If you need to change this option, then use the following
- table to set the proper value in the example above:
- 0 - 256ms
- 1 - 128ms
- 2 - 64ms
- 3 - 32ms
-
- "aic7xxx=panic_on_abort" - This option is for debugging and will cause
- the driver to panic the linux kernel and freeze the system the first
- time the drivers abort or reset routines are called. This is most
- helpful when some problem causes infinite reset loops that scroll too
- fast to see. By using this option, you can write down what the errors
- actually are and send that information to me so it can be fixed.
-
- "aic7xxx=dump_card" - This option will print out the *entire* set of
- configuration registers on the card during the init sequence. This
- is a debugging aid used to see exactly what state the card is in
- when we finally finish our initialization routines. If you don't
- have documentation on the chipsets, this will do you absolutely
- no good unless you are simply trying to write all the information
- down in order to send it to me.
-
- "aic7xxx=dump_sequencer" - This is the same as the above options except
- that instead of dumping the register contents on the card, this
- option dumps the contents of the sequencer program RAM. This gives
- the ability to verify that the instructions downloaded to the
- card's sequencer are indeed what they are suppossed to be. Again,
- unless you have documentation to tell you how to interpret these
- numbers, then it is totally useless.
-
- "aic7xxx=override_term:0xffffffff" - This option is used to force the
- termination on your SCSI controllers to a particular setting. This
- is a bit mask variable that applies for up to 8 aic7xxx SCSI channels.
- Each channel gets 4 bits, divided as follows:
- bit 3 2 1 0
- | | | Enable/Disable Single Ended Low Byte Termination
- | | En/Disable Single Ended High Byte Termination
- | En/Disable Low Byte LVD Termination
- En/Disable High Byte LVD Termination
-
- The upper 2 bits that deal with LVD termination only apply to Ultra2
- controllers. Futhermore, due to the current Ultra2 controller
- designs, these bits are tied together such that setting either bit
- enables both low and high byte LVD termination. It is not possible
- to only set high or low byte LVD termination in this manner. This is
- an artifact of the BIOS definition on Ultra2 controllers. For other
- controllers, the only important bits are the two lowest bits. Setting
- the higher bits on non-Ultra2 controllers has no effect. A few
- examples of how to use this option:
-
- Enable low and high byte termination on a non-ultra2 controller that
- is the first aic7xxx controller (the correct bits are 0011),
- aic7xxx=override_term:0x3
-
- Enable all termination on the third aic7xxx controller, high byte
- termination on the second aic7xxx controller, and low and high byte
- SE termination on the first aic7xxx controller
- (bits are 1111 0010 0011),
- aic7xxx=override_term:0xf23
-
- No attempt has been made to make this option non-cryptic. It really
- shouldn't be used except in dire circumstances, and if that happens,
- I'm probably going to be telling you what to set this to anyway :)
-
- "aic7xxx=stpwlev:0xffffffff" - This option is used to control the STPWLEV
- bit in the DEVCONFIG PCI register. Currently, this is one of the
- very few registers that we have absolutely *no* way of detecting
- what the variable should be. It depends entirely on how the chipset
- and external terminators were coupled by the card/motherboard maker.
- Further, a chip reset (at power up) always sets this bit to 0. If
- there is no BIOS to run on the chipset/card (such as with a 2910C
- or a motherboard controller with the BIOS totally disabled) then
- the variable may not get set properly. Of course, if the proper
- setting was 0, then that's what it would be after the reset, but if
- the proper setting is actually 1.....you get the picture. Now, since
- we can't detect this at all, I've added this option to force the
- setting. If you have a BIOS on your controller then you should never
- need to use this option. However, if you are having lots of SCSI
- reset problems and can't seem to get them knocked out, this may help.
-
- Here's a test to know for certain if you need this option. Make
- a boot floppy that you can use to boot your computer up and that
- will detect the aic7xxx controller. Next, power down your computer.
- While it's down, unplug all SCSI cables from your Adaptec SCSI
- controller. Boot the system back up to the Adaptec EZ-SCSI BIOS
- and then make sure that termination is enabled on your adapter (if
- you have an Adaptec BIOS of course). Next, boot up the floppy you
- made and wait for it to detect the aic7xxx controller. If the kernel
- finds the controller fine, says scsi : x hosts and then tries to
- detect your devices like normal, up to the point where it fails to
- mount your root file system and panics, then you're fine. If, on
- the other hand, the system goes into an infinite reset loop, then
- you need to use this option and/or the previous option to force the
- proper termination settings on your controller. If this happens,
- then you next need to figure out what your settings should be.
-
- To find the correct settings, power your machine back down, connect
- back up the SCSI cables, and boot back into your machine like normal.
- However, boot with the aic7xxx=verbose:0x39 option. Record the
- initial DEVCONFIG values for each of your aic7xxx controllers as
- they are listed, and also record what the machine is detecting as
- the proper termination on your controllers. NOTE: the order in
- which the initial DEVCONFIG values are printed out is not gauranteed
- to be the same order as the SCSI controllers are registered. The
- above option and this option both work on the order of the SCSI
- controllers as they are registered, so make sure you match the right
- DEVCONFIG values with the right controllers if you have more than
- one aic7xxx controller.
-
- Once you have the detected termination settings and the initial
- DEVCONFIG values for each controller, then figure out what the
- termination on each of the controllers *should* be. Hopefully, that
- part is correct, but it could possibly be wrong if there is
- bogus cable detection logic on your controller or something similar.
- If all the controllers have the correct termination settings, then
- don't set the aic7xxx=override_term variable at all, leave it alone.
- Next, on any controllers that go into an infinite reset loop when
- you unplug all the SCSI cables, get the starting DEVCONFIG value.
- If the initial DEVCONFIG value is divisible by 2, then the correct
- setting for that controller is 0. If it's an odd number, then
- the correct setting for that controller is 1. For any other
- controllers that didn't have an infinite reset problem, then reverse
- the above options. If DEVCONFIG was even, then the correct setting
- is 1, if not then the correct setting is 0.
-
- Now that you know what the correct setting was for each controller,
- we need to encode that into the aic7xxx=stpwlev:0x... variable.
- This variable is a bit field encoded variable. Bit 0 is for the first
- aic7xxx controller, bit 1 for the next, etc. Put all these bits
- together and you get a number. For example, if the third aic7xxx
- needed a 1, but the second and first both needed a 0, then the bits
- would be 100 in binary. This then translates to 0x04. You would
- therefore set aic7xxx=stpwlev:0x04. This is fairly standard binary
- to hexadecimal conversions here. If you aren't up to speed on the
- binary->hex conversion then send an email to the aic7xxx mailing
- list and someone can help you out.
-
- "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable
- or enable Tagged Command Queueing (TCQ) on specific devices. As of
- driver version 5.1.11, TCQ is now either on or off by default
- according to the setting you choose during the make config process.
- In order to en/disable TCQ for certian devices at boot time, a user
- may use this boot param. The driver will then parse this message out
- and en/disable the specific device entries that are present based upon
- the value given. The param line is parsed in the following manner:
-
- { - first instance indicates the start of this parameter values
- second instance is the start of entries for a particular
- device entry
- } - end the entries for a particular host adapter, or end the entire
- set of parameter entries
- , - move to next entry. Inside of a set of device entries, this
- moves us to the next device on the list. Outside of device
- entries, this moves us to the next host adapter
- . - Same effect as , but is safe to use with insmod.
- x - the number to enter into the array at this position.
- 0 = Enable tagged queueing on this device and use the default
- queue depth
- 1-254 = Enable tagged queueing on this device and use this
- number as the queue depth
- 255 = Disable tagged queueing on this device.
- Note: anything above 32 for an actual queue depth is wasteful
- and not recommended.
-
- A few examples of how this can be used:
-
- tag_info:{{8,12,,0,,255,4}}
- This line will only effect the first aic7xxx card registered. It
- will set scsi id 0 to a queue depth of 8, id 1 to 12, leave id 2
- at the default, set id 3 to tagged queueing enabled and use the
- default queue depth, id 4 default, id 5 disabled, and id 6 to 4.
- Any not specified entries stay at the default value, repeated
- commas with no value specified will simply increment to the next id
- without changing anything for the missing values.
-
- tag_info:{,,,{,,,255}}
- First, second, and third adapters at default values. Fourth
- adapter, id 3 is disabled. Notice that leading commas simply
- increment what the first number effects, and there are no need
- for trailing commas. When you close out an adapter, or the
- entire entry, anything not explicitly set stays at the default
- value.
-
- A final note on this option. The scanner I used for this isn't
- perfect or highly robust. If you mess the line up, the worst that
- should happen is that the line will get ignored. If you don't
- close out the entire entry with the final bracket, then any other
- aic7xxx options after this will get ignored. So, in general, be
- sure of what you are entering, and after you have it right, just
- add it to the lilo.conf file so there won't be any mistakes. As
- a means of checking this parser, the entire tag_info array for
- each card is now printed out in the /proc/scsi/aic7xxx/x file. You
- can use that to verify that your options were parsed correctly.
-
- Boot command line options may be combined to form the proper set of options
- a user might need. For example, the following is valid:
-
- aic7xxx=verbose,extended,irq_trigger:1
-
- The only requirement is that individual options be separated by a comma or
- a period on the command line.
-
- Module Loading command options
- ------------------------------
- When loading the aic7xxx driver as a module, the exact same options are
- available to the user. However, the syntax to specify the options changes
- slightly. For insmod, you need to wrap the aic7xxx= argument in quotes
- and replace all ',' with '.'. So, for example, a valid insmod line
- would be:
-
- insmod aic7xxx aic7xxx='verbose.irq_trigger:1.extended'
-
- This line should result in the *exact* same behaviour as if you typed
- it in at the lilo prompt and the driver was compiled into the kernel
- instead of being a module. The reason for the single quote is so that
- the shell won't try to interpret anything in the line, such as {.
- Insmod assumes any options starting with a letter instead of a number
- is a character string (which is what we want) and by switching all of
- the commas to periods, insmod won't interpret this as more than one
- string and write junk into our binary image. I consider it a bug in
- the insmod program that even if you wrap your string in quotes (quotes
- that pass the shell mind you and that insmod sees) it still treates
- a comma inside of those quotes as starting a new variable, resulting
- in memory scribbles if you don't switch the commas to periods.
-
-
- Kernel Compile options
- ------------------------------
- The various kernel compile time options for this driver are now fairly
- well documented in the file Documentation/Configure.help. In order to
- see this documentation, you need to use one of the advanced configuration
- programs (menuconfig and xconfig). If you are using the "make menuconfig"
- method of configuring your kernel, then you would simply highlight the
- option in question and hit the ? key. If you are using the "make xconfig"
- method of configuring your kernel, then simply click on the help button
- next to the option you have questions about. The help information from
- the Configure.help file will then get automatically displayed.
-
- /proc support
- ------------------------------
- The /proc support for the AIC7xxx can be found in the /proc/scsi/aic7xxx/
- directory. That directory contains a file for each SCSI controller in
- the system. Each file presents the current configuration and transfer
- statistics (enabled with #define in aic7xxx.c) for each controller.
-
- Thanks to Michael Neuffer for his upper-level SCSI help, and
- Matthew Jacob for statistics support.
-
- Debugging the driver
- ------------------------------
- Should you have problems with this driver, and would like some help in
- getting them solved, there are a couple debugging items built into
- the driver to facilitate getting the needed information from the system.
- In general, I need a complete description of the problem, with as many
- logs as possible concerning what happens. To help with this, there is
- a command option aic7xxx=panic_on_abort. This option, when set, forces
- the driver to panic the kernel on the first SCSI abort issued by the
- mid level SCSI code. If your system is going to reset loops and you
- can't read the screen, then this is what you need. Not only will it
- stop the system, but it also prints out a large amount of state
- information in the process. Second, if you specify the option
- "aic7xxx=verbose:0x1ffff", the system will print out *SOOOO* much
- information as it runs that you won't be able to see anything.
- However, this can actually be very useful if your machine simply
- locks up when trying to boot, since it will pin-point what was last
- happening (in regards to the aic7xxx driver) immediately prior to
- the lockup. This is really only useful if your machine simply can
- not boot up successfully. If you can get your machine to run, then
- this will produce far too much information.
-
- FTP sites
- ------------------------------
- ftp://ftp.redhat.com/pub/aic/
- - Out of date. I used to keep stuff here, but too many people
- complained about having a hard time getting into Red Hat's ftp
- server. So use the web site below instead.
- ftp://ftp.pcnet.com/users/eischen/Linux/
- - Dan Eischen's driver distribution area
- ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
- - European Linux mirror of Teleport site
-
- Web sites
- ------------------------------
- http://people.redhat.com/dledford/
- - My web site, also the primary aic7xxx site with several related
- pages.
-
-Dean W. Gehnert
-deang@teleport.com
-
-$Revision: 3.0 $
-
-Modified by Doug Ledford 1998-2000
-
+====================================================================
+= Adaptec Aic7xxx Fast -> Ultra160 Family Manager Set v6.2.10 =
+= README for =
+= The Linux Operating System =
+====================================================================
+
+The following information is available in this file:
+
+ 1. Supported Hardware
+ 2. Command Line Options
+ 3. Contacting Adaptec
+
+1. Supported Hardware
+
+ The following Adaptec SCSI Chips and Host Adapters are supported by
+ the aic7xxx driver.
+
+ Chip MIPS Host Bus MaxSync MaxWidth SCBs Notes
+ ---------------------------------------------------------------
+ aic7770 10 EISA/VL 10MHz 16Bit 4 1
+ aic7850 10 PCI/32 10MHz 8Bit 3
+ aic7855 10 PCI/32 10MHz 8Bit 3
+ aic7856 10 PCI/32 10MHz 8Bit 3
+ aic7859 10 PCI/32 20MHz 8Bit 3
+ aic7860 10 PCI/32 20MHz 8Bit 3
+ aic7870 10 PCI/32 10MHz 16Bit 16
+ aic7880 10 PCI/32 20MHz 16Bit 16
+ aic7890 20 PCI/32 40MHz 16Bit 16 3 4 5 6 7 8
+ aic7891 20 PCI/64 40MHz 16Bit 16 3 4 5 6 7 8
+ aic7892 20 PCI/64-66 80MHz 16Bit 16 3 4 5 6 7 8
+ aic7895 15 PCI/32 20MHz 16Bit 16 2 3 4 5
+ aic7895C 15 PCI/32 20MHz 16Bit 16 2 3 4 5 8
+ aic7896 20 PCI/32 40MHz 16Bit 16 2 3 4 5 6 7 8
+ aic7897 20 PCI/64 40MHz 16Bit 16 2 3 4 5 6 7 8
+ aic7899 20 PCI/64-66 80MHz 16Bit 16 2 3 4 5 6 7 8
+
+ 1. Multiplexed Twin Channel Device - One controller servicing two
+ busses.
+ 2. Multi-function Twin Channel Device - Two controllers on one chip.
+ 3. Command Channel Secondary DMA Engine - Allows scatter gather list
+ and SCB prefetch.
+ 4. 64 Byte SCB Support - Allows disconnected, unttagged request table
+ for all possible target/lun combinations.
+ 5. Block Move Instruction Support - Doubles the speed of certain
+ sequencer operations.
+ 6. `Bayonet' style Scatter Gather Engine - Improves S/G prefetch
+ performance.
+ 7. Queuing Registers - Allows queuing of new transactions without
+ pausing the sequencer.
+ 8. Multiple Target IDs - Allows the controller to respond to selection
+ as a target on multiple SCSI IDs.
+
+ Controller Chip Host-Bus Int-Connectors Ext-Connectors Notes
+ --------------------------------------------------------------------------
+ AHA-274X[A] aic7770 EISA SE-50M SE-HD50F
+ AHA-274X[A]W aic7770 EISA SE-HD68F SE-HD68F
+ SE-50M
+ AHA-274X[A]T aic7770 EISA 2 X SE-50M SE-HD50F
+ AHA-2842 aic7770 VL SE-50M SE-HD50F
+ AHA-2940AU aic7860 PCI/32 SE-50M SE-HD50F
+ AVA-2902I aic7860 PCI/32 SE-50M
+ AVA-2902E aic7860 PCI/32 SE-50M
+ AVA-2906 aic7856 PCI/32 SE-50M SE-DB25F
+ APC-7850 aic7850 PCI/32 SE-50M 1
+ AVA-2940 aic7860 PCI/32 SE-50M
+ AHA-2920B aic7860 PCI/32 SE-50M
+ AHA-2930B aic7860 PCI/32 SE-50M
+ AHA-2920C aic7856 PCI/32 SE-50M SE-HD50F
+ AHA-2930C aic7860 PCI/32 SE-50M
+ AHA-2930C aic7860 PCI/32 SE-50M
+ AHA-2910C aic7860 PCI/32 SE-50M
+ AHA-2915C aic7860 PCI/32 SE-50M
+ AHA-2940AU/CN aic7860 PCI/32 SE-50M SE-HD50F
+ AHA-2944W aic7870 PCI/32 HVD-HD68F HVD-HD68F
+ HVD-50M
+ AHA-3940W aic7870 PCI/32 2 X SE-HD68F SE-HD68F 2
+ AHA-2940UW aic7880 PCI/32 SE-HD68F
+ SE-50M SE-HD68F
+ AHA-2940U aic7880 PCI/32 SE-50M SE-HD50F
+ AHA-2940D aic7880 PCI/32
+ AHA-2940 A/T aic7880 PCI/32
+ AHA-2940D A/T aic7880 PCI/32
+ AHA-3940UW aic7880 PCI/32 2 X SE-HD68F SE-HD68F 3
+ AHA-3940UWD aic7880 PCI/32 2 X SE-HD68F 2 X SE-VHD68F 3
+ AHA-3940U aic7880 PCI/32 2 X SE-50M SE-HD50F 3
+ AHA-2944UW aic7880 PCI/32 HVD-HD68F HVD-HD68F
+ HVD-50M
+ AHA-3944UWD aic7880 PCI/32 2 X HVD-HD68F 2 X HVD-VHD68F 3
+ AHA-4944UW aic7880 PCI/32
+ AHA-2930UW aic7880 PCI/32
+ AHA-2940UW Pro aic7880 PCI/32 SE-HD68F SE-HD68F 4
+ SE-50M
+ AHA-2940UW/CN aic7880 PCI/32
+ AHA-2940UDual aic7895 PCI/32
+ AHA-2940UWDual aic7895 PCI/32
+ AHA-3940UWD aic7895 PCI/32
+ AHA-3940AUW aic7895 PCI/32
+ AHA-3940AUWD aic7895 PCI/32
+ AHA-3940AU aic7895 PCI/32
+ AHA-3944AUWD aic7895 PCI/32 2 X HVD-HD68F 2 X HVD-VHD68F
+ AHA-2940U2B aic7890 PCI/32 LVD-HD68F LVD-HD68F
+ AHA-2940U2 OEM aic7891 PCI/64
+ AHA-2940U2W aic7890 PCI/32 LVD-HD68F LVD-HD68F
+ SE-HD68F
+ SE-50M
+ AHA-2950U2B aic7891 PCI/64 LVD-HD68F LVD-HD68F
+ AHA-2930U2 aic7890 PCI/32 LVD-HD68F SE-HD50F
+ SE-50M
+ AHA-3950U2B aic7897 PCI/64
+ AHA-3950U2D aic7897 PCI/64
+ AHA-29160 aic7892 PCI/64-66
+ AHA-29160 CPQ aic7892 PCI/64-66
+ AHA-29160N aic7892 PCI/32 LVD-HD68F SE-HD50F
+ SE-50M
+ AHA-29160LP aic7892 PCI/64-66
+ AHA-19160 aic7892 PCI/64-66
+ AHA-29150LP aic7892 PCI/64-66
+ AHA-29130LP aic7892 PCI/64-66
+ AHA-3960D aic7899 PCI/64-66 2 X LVD-HD68F 2 X LVD-VHD68F
+ LVD-50M
+ AHA-3960D CPQ aic7899 PCI/64-66 2 X LVD-HD68F 2 X LVD-VHD68F
+ LVD-50M
+ AHA-39160 aic7899 PCI/64-66 2 X LVD-HD68F 2 X LVD-VHD68F
+ LVD-50M
+
+ 1. No BIOS support
+ 2. DEC21050 PCI-PCI bridge with multiple controller chips on secondary bus
+ 3. DEC2115X PCI-PCI bridge with multiple controller chips on secondary bus
+ 4. All three SCSI connectors may be used simultaneously without
+ SCSI "stub" effects.
+
+2. Command Line Options
+
+ WARNING: ALTERING OR ADDING THESE DRIVER PARAMETERS
+ INCORRECTLY CAN RENDER YOUR SYSTEM INOPERABLE.
+ USE THEM WITH CAUTION.
+
+ Edit the file "modules.conf" in the directory /etc and add/edit a
+ line containing 'options aic7xxx=[command[,command...]]' where
+ 'command' is one or more of the following:
+ -----------------------------------------------------------------
+ Option: verbose
+ Definition: enable additional informative messages during
+ driver operation.
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: debug:[value]
+ Definition: Enables various levels of debugging information
+ Possible Values: 0x0000 = no debugging, 0xffff = full debugging
+ Default Value: 0x0000
+ -----------------------------------------------------------------
+ Option: no_reset
+ Definition: Do not reset the bus during the initial probe
+ phase
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: extended
+ Definition: Force extended translation on the controller
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: periodic_otag
+ Definition: Send an ordered tag periodically to prevent
+ tag starvation. Needed for some older devices
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: reverse_scan
+ Definition: Probe the scsi bus in reverse order, starting
+ with target 15
+ Possible Values: This option is a flag
+ Default Value: disabled
+ -----------------------------------------------------------------
+ Option: global_tag_depth
+ Definition: Global tag depth for all targets on all busses.
+ This option sets the default tag depth which
+ may be selectively overridden vi the tag_info
+ option.
+ Possible Values: 1 - 253
+ Default Value: 32
+ -----------------------------------------------------------------
+ Option: tag_info:{{value[,value...]}[,{value[,value...]}...]}
+ Definition: Set the per-target tagged queue depth on a
+ per controller basis. Both controllers and targets
+ may be ommitted indicating that they should retain
+ the default tag depth.
+ Examples: tag_info:{{16,32,32,64,8,8,,32,32,32,32,32,32,32,32,32}
+ On Controller 0
+ specifies a tag depth of 16 for target 0
+ specifies a tag depth of 64 for target 3
+ specifies a tag depth of 8 for targets 4 and 5
+ leaves target 6 at the default
+ specifies a tag depth of 32 for targets 1,2,7-15
+ All other targets retain the default depth.
+
+ tag_info:{{},{32,,32}}
+ On Controller 1
+ specifies a tag depth of 32 for targets 0 and 2
+ All other targets retain the default depth.
+
+ Possible Values: 1 - 253
+ Default Value: 32
+ -----------------------------------------------------------------
+ Option: seltime:[value]
+ Definition: Specifies the selection timeout value
+ Possible Values: 0 = 256ms, 1 = 128ms, 2 = 64ms, 3 = 32ms
+ Default Value: 0
+ -----------------------------------------------------------------
+
+ Example: 'options aic7xxx=verbose,no_probe,tag_info:{{},{,,10}},seltime:1"
+ enables verbose logging, Disable EISA/VLB probing,
+ and set tag depth on Controller 1/Target 2 to 10 tags.
+
+3. Contacting Adaptec
+
+ A Technical Support Identification (TSID) Number is required for
+ Adaptec technical support.
+ - The 12-digit TSID can be found on the white barcode-type label
+ included inside the box with your product. The TSID helps us
+ provide more efficient service by accurately identifying your
+ product and support status.
+ Support Options
+ - Search the Adaptec Support Knowledgebase (ASK) at
+ http://ask.adaptec.com for articles, troubleshooting tips, and
+ frequently asked questions for your product.
+ - For support via Email, submit your question to Adaptec's
+ Technical Support Specialists at http://ask.adaptec.com.
+
+ North America
+ - Visit our Web site at http://www.adaptec.com.
+ - To speak with a Fibre Channel/RAID/External Storage Technical
+ Support Specialist, call 1-321-207-2000,
+ Hours: Monday-Friday, 3:00 A.M. to 5:00 P.M., PST.
+ (Not open on holidays)
+ - For Technical Support in all other technologies including
+ SCSI, call 1-408-934-7274,
+ Hours: Monday-Friday, 6:00 A.M. to 5:00 P.M., PST.
+ (Not open on holidays)
+ - For after hours support, call 1-800-416-8066 ($99/call,
+ $149/call on holidays)
+ - To order Adaptec products including software and cables, call
+ 1-800-442-7274 or 1-408-957-7274. You can also visit our
+ online store at http://www.adaptecstore.com
+
+ Europe
+ - Visit our Web site at http://www.adaptec-europe.com.
+ - English and French: To speak with a Technical Support
+ Specialist, call one of the following numbers:
+ - English: +32-2-352-3470
+ - French: +32-2-352-3460
+ Hours: Monday-Thursday, 10:00 to 12:30, 13:30 to 17:30 CET
+ Friday, 10:00 to 12:30, 13:30 to 16:30 CET
+ - German: To speak with a Technical Support Specialist,
+ call +49-89-456-40660
+ Hours: Monday-Thursday, 09:30 to 12:30, 13:30 to 16:30 CET
+ Friday, 09:30 to 12:30, 13:30 to 15:00 CET
+ - To order Adaptec products, including accessories and cables:
+ - UK: +0800-96-65-26 or fax +0800-731-02-95
+ - Other European countries: +32-11-300-379
+
+ Australia and New Zealand
+ - Visit our Web site at http://www.adaptec.com.au.
+ - To speak with a Technical Support Specialist, call
+ +612-9416-0698
+ Hours: Monday-Friday, 10:00 A.M. to 4:30 P.M., EAT
+ (Not open on holidays)
+
+ Japan
+ - To speak with a Technical Support Specialist, call
+ +81-3-5308-6120
+ Hours: Monday-Friday, 9:00 a.m. to 12:00 p.m., 1:00 p.m. to
+ 6:00 p.m. TSC
+
+ Hong Kong and China
+ - To speak with a Technical Support Specialist, call
+ +852-2869-7200
+ Hours: Monday-Friday, 10:00 to 17:00.
+ - Fax Technical Support at +852-2869-7100.
+
+ Singapore
+ - To speak with a Technical Support Specialist, call
+ +65-245-7470
+ Hours: Monday-Friday, 10:00 to 17:00.
+ - Fax Technical Support at +852-2869-7100
+
+-------------------------------------------------------------------
+
+(c) 2002 Adaptec, Inc. All Rights Reserved. No part of this
+publication may be reproduced, stored in a retrieval system, or
+transmitted in any form or by any means, electronic, mechanical,
+photocopying, recording or otherwise, without prior written consent
+of Adaptec, Inc., 691 South Milpitas Blvd., Milpitas, CA 95035.
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
index 864cbfdc40e9..8cb78e902d23 100644
--- a/drivers/scsi/53c700.c
+++ b/drivers/scsi/53c700.c
@@ -238,39 +238,30 @@ struct Scsi_Host *
NCR_700_detect(Scsi_Host_Template *tpnt,
struct NCR_700_Host_Parameters *hostdata)
{
- dma_addr_t pScript, pMemory, pSlots;
+ dma_addr_t pScript, pSlots;
__u8 *memory;
__u32 *script;
struct Scsi_Host *host;
static int banner = 0;
int j;
-#ifdef CONFIG_53C700_USE_CONSISTENT
- memory = pci_alloc_consistent(hostdata->pci_dev, TOTAL_MEM_SIZE,
- &pMemory);
- hostdata->consistent = 1;
- if(memory == NULL ) {
- printk(KERN_WARNING "53c700: consistent memory allocation failed\n");
-#endif
- memory = kmalloc(TOTAL_MEM_SIZE, GFP_KERNEL);
- if(memory == NULL) {
- printk(KERN_ERR "53c700: Failed to allocate memory for driver, detatching\n");
- return NULL;
- }
- pMemory = pci_map_single(hostdata->pci_dev, memory,
- TOTAL_MEM_SIZE, PCI_DMA_BIDIRECTIONAL);
-#ifdef CONFIG_53C700_USE_CONSISTENT
- hostdata->consistent = 0;
+ memory = dma_alloc_noncoherent(hostdata->dev, TOTAL_MEM_SIZE,
+ &pScript);
+ if(memory == NULL) {
+ printk(KERN_ERR "53c700: Failed to allocate memory for driver, detatching\n");
+ return NULL;
}
-#endif
+
script = (__u32 *)memory;
- pScript = pMemory;
hostdata->msgin = memory + MSGIN_OFFSET;
hostdata->msgout = memory + MSGOUT_OFFSET;
hostdata->status = memory + STATUS_OFFSET;
+ /* all of these offsets are L1_CACHE_BYTES separated. It is fatal
+ * if this isn't sufficient separation to avoid dma flushing issues */
+ BUG_ON(!dma_is_consistent(pScript) && L1_CACHE_BYTES < dma_get_cache_alignment());
hostdata->slots = (struct NCR_700_command_slot *)(memory + SLOTS_OFFSET);
- pSlots = pMemory + SLOTS_OFFSET;
+ pSlots = pScript + SLOTS_OFFSET;
/* Fill in the missing routines from the host template */
tpnt->queuecommand = NCR_700_queuecommand;
@@ -327,7 +318,7 @@ NCR_700_detect(Scsi_Host_Template *tpnt,
hostdata->script = script;
hostdata->pScript = pScript;
- NCR_700_dma_cache_wback((unsigned long)script, sizeof(SCRIPT));
+ dma_sync_single(hostdata->dev, pScript, sizeof(SCRIPT), DMA_TO_DEVICE);
hostdata->state = NCR_700_HOST_FREE;
hostdata->cmd = NULL;
host->max_id = 7;
@@ -363,18 +354,8 @@ NCR_700_release(struct Scsi_Host *host)
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)host->hostdata[0];
-#ifdef CONFIG_53C700_USE_CONSISTENT
- if(hostdata->consistent) {
- pci_free_consistent(hostdata->pci_dev, TOTAL_MEM_SIZE,
- hostdata->script, hostdata->pScript);
- } else {
-#endif
- pci_unmap_single(hostdata->pci_dev, hostdata->pScript,
- TOTAL_MEM_SIZE, PCI_DMA_BIDIRECTIONAL);
- kfree(hostdata->script);
-#ifdef CONFIG_53C700_USE_CONSISTENT
- }
-#endif
+ dma_free_noncoherent(hostdata->dev, TOTAL_MEM_SIZE,
+ hostdata->script, hostdata->pScript);
return 1;
}
@@ -575,15 +556,16 @@ NCR_700_unmap(struct NCR_700_Host_Parameters *hostdata, Scsi_Cmnd *SCp,
{
if(SCp->sc_data_direction != SCSI_DATA_NONE &&
SCp->sc_data_direction != SCSI_DATA_UNKNOWN) {
- int pci_direction = scsi_to_pci_dma_dir(SCp->sc_data_direction);
+ enum dma_data_direction direction =
+ (enum dma_data_direction)scsi_to_pci_dma_dir(SCp->sc_data_direction);
if(SCp->use_sg) {
- pci_unmap_sg(hostdata->pci_dev, SCp->buffer,
- SCp->use_sg, pci_direction);
+ dma_unmap_sg(hostdata->dev, SCp->buffer,
+ SCp->use_sg, direction);
} else {
- pci_unmap_single(hostdata->pci_dev,
+ dma_unmap_single(hostdata->dev,
slot->dma_handle,
SCp->request_bufflen,
- pci_direction);
+ direction);
}
}
}
@@ -600,8 +582,8 @@ NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata,
(struct NCR_700_command_slot *)SCp->host_scribble;
NCR_700_unmap(hostdata, SCp, slot);
- pci_unmap_single(hostdata->pci_dev, slot->pCmd,
- sizeof(SCp->cmnd), PCI_DMA_TODEVICE);
+ dma_unmap_single(hostdata->dev, slot->pCmd,
+ sizeof(SCp->cmnd), DMA_TO_DEVICE);
if(SCp->cmnd[0] == REQUEST_SENSE && SCp->cmnd[6] == NCR_700_INTERNAL_SENSE_MAGIC) {
#ifdef NCR_700_DEBUG
printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense is\n",
@@ -819,7 +801,7 @@ process_extended_message(struct Scsi_Host *host,
printk(KERN_WARNING "scsi%d Unexpected SDTR msg\n",
host->host_no);
hostdata->msgout[0] = A_REJECT_MSG;
- NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, 1);
+ dma_cache_sync(hostdata->msgout, 1, DMA_TO_DEVICE);
script_patch_16(hostdata->script, MessageCount, 1);
/* SendMsgOut returns, so set up the return
* address */
@@ -831,7 +813,7 @@ process_extended_message(struct Scsi_Host *host,
printk(KERN_INFO "scsi%d: (%d:%d), Unsolicited WDTR after CMD, Rejecting\n",
host->host_no, pun, lun);
hostdata->msgout[0] = A_REJECT_MSG;
- NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, 1);
+ dma_cache_sync(hostdata->msgout, 1, DMA_TO_DEVICE);
script_patch_16(hostdata->script, MessageCount, 1);
resume_offset = hostdata->pScript + Ent_SendMessageWithATN;
@@ -845,7 +827,7 @@ process_extended_message(struct Scsi_Host *host,
printk("\n");
/* just reject it */
hostdata->msgout[0] = A_REJECT_MSG;
- NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, 1);
+ dma_cache_sync(hostdata->msgout, 1, DMA_TO_DEVICE);
script_patch_16(hostdata->script, MessageCount, 1);
/* SendMsgOut returns, so set up the return
* address */
@@ -923,7 +905,7 @@ process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata
printk("\n");
/* just reject it */
hostdata->msgout[0] = A_REJECT_MSG;
- NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, 1);
+ dma_cache_sync(hostdata->msgout, 1, DMA_TO_DEVICE);
script_patch_16(hostdata->script, MessageCount, 1);
/* SendMsgOut returns, so set up the return
* address */
@@ -933,7 +915,7 @@ process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata
}
NCR_700_writel(temp, host, TEMP_REG);
/* set us up to receive another message */
- NCR_700_dma_cache_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE);
+ dma_cache_sync(hostdata->msgin, MSG_ARRAY_SIZE, DMA_FROM_DEVICE);
return resume_offset;
}
@@ -997,19 +979,17 @@ process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp,
SCp->cmnd[7] = hostdata->status[0];
SCp->use_sg = 0;
SCp->sc_data_direction = SCSI_DATA_READ;
- pci_dma_sync_single(hostdata->pci_dev,
- slot->pCmd,
- SCp->cmd_len,
- PCI_DMA_TODEVICE);
+ dma_sync_single(hostdata->dev, slot->pCmd,
+ SCp->cmd_len, DMA_TO_DEVICE);
SCp->request_bufflen = sizeof(SCp->sense_buffer);
- slot->dma_handle = pci_map_single(hostdata->pci_dev, SCp->sense_buffer, sizeof(SCp->sense_buffer), PCI_DMA_FROMDEVICE);
+ slot->dma_handle = dma_map_single(hostdata->dev, SCp->sense_buffer, sizeof(SCp->sense_buffer), DMA_FROM_DEVICE);
slot->SG[0].ins = bS_to_host(SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer));
slot->SG[0].pAddr = bS_to_host(slot->dma_handle);
slot->SG[1].ins = bS_to_host(SCRIPT_RETURN);
slot->SG[1].pAddr = 0;
slot->resume_offset = hostdata->pScript;
- NCR_700_dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG[0])*2);
- NCR_700_dma_cache_inv((unsigned long)SCp->sense_buffer, sizeof(SCp->sense_buffer));
+ dma_cache_sync(slot->SG, sizeof(slot->SG[0])*2, DMA_TO_DEVICE);
+ dma_cache_sync(SCp->sense_buffer, sizeof(SCp->sense_buffer), DMA_FROM_DEVICE);
/* queue the command for reissue */
slot->state = NCR_700_SLOT_QUEUED;
@@ -1024,10 +1004,10 @@ process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp,
// SCp->cmnd[0] == INQUIRY && SCp->use_sg == 0) {
// /* Piggy back the tag queueing support
// * on this command */
- // pci_dma_sync_single(hostdata->pci_dev,
+ // dma_sync_single(hostdata->dev,
// slot->dma_handle,
// SCp->request_bufflen,
- // PCI_DMA_FROMDEVICE);
+ // DMA_FROM_DEVICE);
// if(((char *)SCp->request_buffer)[7] & 0x02) {
// printk(KERN_INFO "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", host->host_no, pun, lun);
// hostdata->tag_negotiated |= (1<<SCp->target);
@@ -1137,14 +1117,14 @@ process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp,
* should therefore always clear ACK */
NCR_700_writeb(NCR_700_get_SXFER(hostdata->cmd->device),
host, SXFER_REG);
- NCR_700_dma_cache_inv((unsigned long)hostdata->msgin,
- MSG_ARRAY_SIZE);
- NCR_700_dma_cache_wback((unsigned long)hostdata->msgout,
- MSG_ARRAY_SIZE);
+ dma_cache_sync(hostdata->msgin,
+ MSG_ARRAY_SIZE, DMA_FROM_DEVICE);
+ dma_cache_sync(hostdata->msgout,
+ MSG_ARRAY_SIZE, DMA_TO_DEVICE);
/* I'm just being paranoid here, the command should
* already have been flushed from the cache */
- NCR_700_dma_cache_wback((unsigned long)slot->cmnd->cmnd,
- slot->cmnd->cmd_len);
+ dma_cache_sync(slot->cmnd->cmnd,
+ slot->cmnd->cmd_len, DMA_TO_DEVICE);
@@ -1207,8 +1187,8 @@ process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp,
hostdata->reselection_id = reselection_id;
/* just in case we have a stale simple tag message, clear it */
hostdata->msgin[1] = 0;
- NCR_700_dma_cache_wback_inv((unsigned long)hostdata->msgin,
- MSG_ARRAY_SIZE);
+ dma_cache_sync(hostdata->msgin,
+ MSG_ARRAY_SIZE, DMA_BIDIRECTIONAL);
if(hostdata->tag_negotiated & (1<<reselection_id)) {
resume_offset = hostdata->pScript + Ent_GetReselectionWithTag;
} else {
@@ -1323,7 +1303,8 @@ process_selection(struct Scsi_Host *host, __u32 dsp)
hostdata->cmd = NULL;
/* clear any stale simple tag message */
hostdata->msgin[1] = 0;
- NCR_700_dma_cache_wback_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE);
+ dma_cache_sync(hostdata->msgin, MSG_ARRAY_SIZE,
+ DMA_BIDIRECTIONAL);
if(id == 0xff) {
/* Selected as target, Ignore */
@@ -1434,10 +1415,11 @@ NCR_700_start_command(Scsi_Cmnd *SCp)
if(slot->resume_offset == 0)
slot->resume_offset = hostdata->pScript;
/* now perform all the writebacks and invalidates */
- NCR_700_dma_cache_wback((unsigned long)hostdata->msgout, count);
- NCR_700_dma_cache_inv((unsigned long)hostdata->msgin, MSG_ARRAY_SIZE);
- NCR_700_dma_cache_wback((unsigned long)SCp->cmnd, SCp->cmd_len);
- NCR_700_dma_cache_inv((unsigned long)hostdata->status, 1);
+ dma_cache_sync(hostdata->msgout, count, DMA_TO_DEVICE);
+ dma_cache_sync(hostdata->msgin, MSG_ARRAY_SIZE,
+ DMA_FROM_DEVICE);
+ dma_cache_sync(SCp->cmnd, SCp->cmd_len, DMA_TO_DEVICE);
+ dma_cache_sync(hostdata->status, 1, DMA_FROM_DEVICE);
/* set the synchronous period/offset */
NCR_700_writeb(NCR_700_get_SXFER(SCp->device),
@@ -1606,7 +1588,7 @@ NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs)
slot->SG[i].ins = bS_to_host(SCRIPT_NOP);
slot->SG[i].pAddr = 0;
}
- NCR_700_dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG));
+ dma_cache_sync(slot->SG, sizeof(slot->SG), DMA_TO_DEVICE);
/* and pretend we disconnected after
* the command phase */
resume_offset = hostdata->pScript + Ent_MsgInDuringData;
@@ -1755,7 +1737,7 @@ NCR_700_queuecommand(Scsi_Cmnd *SCp, void (*done)(Scsi_Cmnd *))
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)SCp->host->hostdata[0];
__u32 move_ins;
- int pci_direction;
+ enum dma_data_direction direction;
struct NCR_700_command_slot *slot;
if(hostdata->command_slot_count >= NCR_700_COMMAND_SLOTS_PER_HOST) {
@@ -1883,7 +1865,7 @@ NCR_700_queuecommand(Scsi_Cmnd *SCp, void (*done)(Scsi_Cmnd *))
}
/* now build the scatter gather list */
- pci_direction = scsi_to_pci_dma_dir(SCp->sc_data_direction);
+ direction = (enum dma_data_direction)scsi_to_pci_dma_dir(SCp->sc_data_direction);
if(move_ins != 0) {
int i;
int sg_count;
@@ -1891,13 +1873,13 @@ NCR_700_queuecommand(Scsi_Cmnd *SCp, void (*done)(Scsi_Cmnd *))
__u32 count = 0;
if(SCp->use_sg) {
- sg_count = pci_map_sg(hostdata->pci_dev, SCp->buffer,
- SCp->use_sg, pci_direction);
+ sg_count = dma_map_sg(hostdata->dev, SCp->buffer,
+ SCp->use_sg, direction);
} else {
- vPtr = pci_map_single(hostdata->pci_dev,
+ vPtr = dma_map_single(hostdata->dev,
SCp->request_buffer,
SCp->request_bufflen,
- pci_direction);
+ direction);
count = SCp->request_bufflen;
slot->dma_handle = vPtr;
sg_count = 1;
@@ -1920,14 +1902,14 @@ NCR_700_queuecommand(Scsi_Cmnd *SCp, void (*done)(Scsi_Cmnd *))
}
slot->SG[i].ins = bS_to_host(SCRIPT_RETURN);
slot->SG[i].pAddr = 0;
- NCR_700_dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG));
+ dma_cache_sync(slot->SG, sizeof(slot->SG), DMA_TO_DEVICE);
DEBUG((" SETTING %08lx to %x\n",
(&slot->pSG[i].ins),
slot->SG[i].ins));
}
slot->resume_offset = 0;
- slot->pCmd = pci_map_single(hostdata->pci_dev, SCp->cmnd,
- sizeof(SCp->cmnd), PCI_DMA_TODEVICE);
+ slot->pCmd = dma_map_single(hostdata->dev, SCp->cmnd,
+ sizeof(SCp->cmnd), DMA_TO_DEVICE);
NCR_700_start_command(SCp);
return 0;
}
@@ -2018,5 +2000,3 @@ NCR_700_slave_destroy(Scsi_Device *SDp)
EXPORT_SYMBOL(NCR_700_detect);
EXPORT_SYMBOL(NCR_700_release);
EXPORT_SYMBOL(NCR_700_intr);
-
-no_module_init;
diff --git a/drivers/scsi/53c700.h b/drivers/scsi/53c700.h
index f01bdb2e1321..8711876cd83d 100644
--- a/drivers/scsi/53c700.h
+++ b/drivers/scsi/53c700.h
@@ -45,25 +45,6 @@
#error "Config.in must define either CONFIG_53C700_IO_MAPPED or CONFIG_53C700_MEM_MAPPED to use this scsi core."
#endif
-/* macros for consistent memory allocation */
-
-#ifdef CONFIG_53C700_USE_CONSISTENT
-#define NCR_700_dma_cache_wback(mem, size) \
- if(!hostdata->consistent) \
- dma_cache_wback(mem, size)
-#define NCR_700_dma_cache_inv(mem, size) \
- if(!hostdata->consistent) \
- dma_cache_inv(mem, size)
-#define NCR_700_dma_cache_wback_inv(mem, size) \
- if(!hostdata->consistent) \
- dma_cache_wback_inv(mem, size)
-#else
-#define NCR_700_dma_cache_wback(mem, size) dma_cache_wback(mem,size)
-#define NCR_700_dma_cache_inv(mem, size) dma_cache_inv(mem,size)
-#define NCR_700_dma_cache_wback_inv(mem, size) dma_cache_wback_inv(mem,size)
-#endif
-
-
struct NCR_700_Host_Parameters;
/* These are the externally used routines */
@@ -215,7 +196,7 @@ struct NCR_700_Host_Parameters {
/* These must be filled in by the calling driver */
int clock; /* board clock speed in MHz */
unsigned long base; /* the base for the port (copied to host) */
- struct pci_dev *pci_dev;
+ struct device *dev;
__u32 dmode_extra; /* adjustable bus settings */
__u32 differential:1; /* if we are differential */
#ifdef CONFIG_53C700_LE_ON_BE
@@ -229,10 +210,6 @@ struct NCR_700_Host_Parameters {
/* NOTHING BELOW HERE NEEDS ALTERING */
__u32 fast:1; /* if we can alter the SCSI bus clock
speed (so can negiotiate sync) */
-#ifdef CONFIG_53C700_USE_CONSISTENT
- __u32 consistent:1;
-#endif
-
int sync_clock; /* The speed of the SYNC core */
__u32 *script; /* pointer to script location */
@@ -442,7 +419,7 @@ struct NCR_700_Host_Parameters {
for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
__u32 val = bS_to_cpu((script)[A_##symbol##_used[i]]) + value; \
(script)[A_##symbol##_used[i]] = bS_to_host(val); \
- dma_cache_wback((unsigned long)&(script)[A_##symbol##_used[i]], 4); \
+ dma_cache_sync(&(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
DEBUG((" script, patching %s at %d to 0x%lx\n", \
#symbol, A_##symbol##_used[i], (value))); \
} \
@@ -453,7 +430,7 @@ struct NCR_700_Host_Parameters {
int i; \
for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
(script)[A_##symbol##_used[i]] = bS_to_host(value); \
- dma_cache_wback((unsigned long)&(script)[A_##symbol##_used[i]], 4); \
+ dma_cache_sync(&(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
DEBUG((" script, patching %s at %d to 0x%lx\n", \
#symbol, A_##symbol##_used[i], (value))); \
} \
@@ -468,7 +445,7 @@ struct NCR_700_Host_Parameters {
val &= 0xff00ffff; \
val |= ((value) & 0xff) << 16; \
(script)[A_##symbol##_used[i]] = bS_to_host(val); \
- dma_cache_wback((unsigned long)&(script)[A_##symbol##_used[i]], 4); \
+ dma_cache_sync(&(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
DEBUG((" script, patching ID field %s at %d to 0x%x\n", \
#symbol, A_##symbol##_used[i], val)); \
} \
@@ -482,7 +459,7 @@ struct NCR_700_Host_Parameters {
val &= 0xffff0000; \
val |= ((value) & 0xffff); \
(script)[A_##symbol##_used[i]] = bS_to_host(val); \
- dma_cache_wback((unsigned long)&(script)[A_##symbol##_used[i]], 4); \
+ dma_cache_sync(&(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
DEBUG((" script, patching short field %s at %d to 0x%x\n", \
#symbol, A_##symbol##_used[i], val)); \
} \
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index cf6d96290144..55f19eeb38fc 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -277,15 +277,10 @@ config SCSI_AACRAID
tristate "Adaptec AACRAID support (EXPERIMENTAL)"
depends on EXPERIMENTAL && SCSI && PCI
-choice
- prompt "Adaptec AIC7xxx support"
- optional
- depends on SCSI
-
-source "drivers/scsi/aic7xxx/Kconfig"
+source "drivers/scsi/aic7xxx/Kconfig.aic7xxx"
config SCSI_AIC7XXX_OLD
- tristate "Old driver"
+ tristate "Adaptec AIC7xxx support (old driver)"
help
WARNING This driver is an older aic7xxx driver and is no longer
under active development. Adaptec, Inc. is writing a new driver to
@@ -325,8 +320,7 @@ config SCSI_AIC7XXX_OLD
say M here and read <file:Documentation/modules.txt>. The module
will be called aic7xxx_old.o.
-endchoice
-
+source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
# All the I2O code and drivers do not seem to be 64bit safe.
config SCSI_DPT_I2O
@@ -900,11 +894,6 @@ config 53C700_LE_ON_BE
depends on SCSI_LASI700
default y
-config 53C700_USE_CONSISTENT
- bool
- depends on SCSI_LASI700
- default y
-
config SCSI_NCR53C7xx
tristate "NCR53c7,8xx SCSI support"
depends on SCSI && PCI
@@ -1550,24 +1539,18 @@ config SCSI_NSP32
say M here and read <file:Documentation/modules.txt>. The module
will be called nsp32.o.
-#
-# Note - this is a very special 'host' adapter that simulates the presence of some disks.
-# It can come in very handy for troubleshooting. Anyone else is welcome to use it - all
-# you do is hack it to simulate the condition you want to test for, and then use it.
-#
-# The actual configuration in any kernel release could change at any time as I hack it to
-# simulate various conditions that I am testing.
-#
config SCSI_DEBUG
- tristate "SCSI debugging host simulator (EXPERIMENTAL)"
- depends on EXPERIMENTAL && SCSI
- help
- This is a host adapter simulator that can be programmed to simulate
- a large number of conditions that could occur on a real bus. The
- advantage is that many hard to reproduce problems can be tested in a
- controlled environment where there is reduced risk of losing
- important data. This is primarily of use to people trying to debug
- the middle and upper layers of the SCSI subsystem. If unsure, say N.
+ tristate "SCSI debugging host simulator"
+ depends on SCSI
+ help
+ This is a host adapter simulator that can simulate multiple hosts
+ each with multiple dummy SCSI devices (disks). It defaults to one
+ host adapter with one dummy SCSI disk. Each dummy disk uses kernel
+ RAM as storage (i.e. it is a ramdisk). To save space when multiple
+ dummy disks are simulated, they share the same kernel RAM for
+ their storage. See http://www.torque.net/sg/sdebug.html for more
+ information. This driver is primarily of use to those testing the
+ SCSI and block subsystems. If unsure, say N.
config SCSI_MESH
tristate "MESH (Power Mac internal SCSI) support"
diff --git a/drivers/scsi/NCR_D700.c b/drivers/scsi/NCR_D700.c
index 3fb6a3ca8dbd..7b0e05db4676 100644
--- a/drivers/scsi/NCR_D700.c
+++ b/drivers/scsi/NCR_D700.c
@@ -307,6 +307,7 @@ NCR_D700_probe(struct device *dev)
continue;
}
scsi_set_device(host, dev);
+ hostdata->dev = dev;
found++;
}
info->found += found;
diff --git a/drivers/scsi/aic7xxx/Kconfig b/drivers/scsi/aic7xxx/Kconfig
deleted file mode 100644
index fd2cf075cbe1..000000000000
--- a/drivers/scsi/aic7xxx/Kconfig
+++ /dev/null
@@ -1,53 +0,0 @@
-config SCSI_AIC7XXX
- tristate "New driver"
- help
- This driver supports all of Adaptec's PCI based SCSI controllers
- (not the hardware RAID controllers though) as well as the aic7770
- based EISA and VLB SCSI controllers (the 274x and 284x series).
- This is an Adaptec sponsored driver written by Justin Gibbs. It is
- intended to replace the previous aic7xxx driver maintained by Doug
- Ledford since Doug is no longer maintaining that driver.
-
-
-config AIC7XXX_CMDS_PER_DEVICE
- int "Maximum number of TCQ commands per device"
- depends on SCSI_AIC7XXX
- default "253"
- ---help---
- Specify the number of commands you would like to allocate per SCSI
- device when Tagged Command Queueing (TCQ) is enabled on that device.
-
- This is an upper bound value for the number of tagged transactions
- to be used for any device. The aic7xxx driver will automatically
- vary this number based on device behavior. For devices with a
- fixed maximum, the driver will eventually lock to this maximum
- and display a console message inidicating this value.
-
- Note: Unless you experience some type of device failure, the default
- value, no enforced limit, should work for you.
-
- Default: 253
-
-config AIC7XXX_RESET_DELAY_MS
- int "Initial bus reset delay in milli-seconds"
- depends on SCSI_AIC7XXX
- default "15000"
- help
- The number of milliseconds to delay after an initial bus reset.
- The bus settle delay following all error recovery actions is
- dictated by the SCSI layer and is not affected by this value.
-
- Default: 15000 (15 seconds)
-
-config AIC7XXX_BUILD_FIRMWARE
- bool "Build Adapter Firmware with Kernel Build"
- depends on SCSI_AIC7XXX
- help
- This option should only be enabled if you are modifying the firmware
- source to the aic7xxx driver and wish to have the generated firmware
- include files updated during a normal kernel build. The assembler
- for the firmware requires lex and yacc or their equivalents, as well
- as the db v1 library. You may have to install additional packages
- or modify the assembler make file or the files it includes if your
- build environment is different than that of the author.
-
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic79xx b/drivers/scsi/aic7xxx/Kconfig.aic79xx
new file mode 100644
index 000000000000..c2404c8433f4
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Kconfig.aic79xx
@@ -0,0 +1,96 @@
+#
+# AIC79XX 2.5.X Kernel configuration File.
+# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Kconfig.aic79xx#2 $
+#
+config SCSI_AIC79XX
+ tristate "Adaptec AIC79xx U320 support"
+ depends on PCI
+ help
+ This driver supports all of Adaptec's Ultra 320 PCI-X
+ based SCSI controllers.
+
+config AIC79XX_CMDS_PER_DEVICE
+ int "Maximum number of TCQ commands per device"
+ depends on SCSI_AIC79XX
+ default "32"
+ ---help---
+ Specify the number of commands you would like to allocate per SCSI
+ device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+ This is an upper bound value for the number of tagged transactions
+ to be used for any device. The aic7xxx driver will automatically
+ vary this number based on device behavior. For devices with a
+ fixed maximum, the driver will eventually lock to this maximum
+ and display a console message inidicating this value.
+
+ Due to resource allocation issues in the Linux SCSI mid-layer, using
+ a high number of commands per device may result in memory allocation
+ failures when many devices are attached to the system. For this reason,
+ the default is set to 32. Higher values may result in higer performance
+ on some devices. The upper bound is 253. 0 disables tagged queueing.
+
+ Per device tag depth can be controlled via the kernel command line
+ "tag_info" option. See drivers/scsi/aic7xxx/README.aic79xx
+ for details.
+
+config AIC79XX_RESET_DELAY_MS
+ int "Initial bus reset delay in milli-seconds"
+ depends on SCSI_AIC79XX
+ default "15000"
+ ---help---
+ The number of milliseconds to delay after an initial bus reset.
+ The bus settle delay following all error recovery actions is
+ dictated by the SCSI layer and is not affected by this value.
+
+ Default: 15000 (15 seconds)
+
+config AIC79XX_BUILD_FIRMWARE
+ bool "Build Adapter Firmware with Kernel Build"
+ depends on SCSI_AIC79XX
+ help
+ This option should only be enabled if you are modifying the firmware
+ source to the aic79xx driver and wish to have the generated firmware
+ include files updated during a normal kernel build. The assembler
+ for the firmware requires lex and yacc or their equivalents, as well
+ as the db v1 library. You may have to install additional packages
+ or modify the assembler Makefile or the files it includes if your
+ build environment is different than that of the author.
+
+config AIC79XX_ENABLE_RD_STRM
+ bool "Enable Read Streaming for All Targets"
+ depends on SCSI_AIC79XX
+ help
+ Read Streaming is a U320 protocol option that should enhance
+ performance. Early U320 drive firmware actually performs slower
+ with read streaming enabled so it is disabled by default. Read
+ Streaming can be configured in much the same way as tagged queueing
+ using the "rd_strm" command line option. See
+ drivers/scsi/aic7xxx/README.aic79xx for details.
+
+config AIC79XX_DEBUG_ENABLE
+ bool "Compile in Debugging Code"
+ depends on SCSI_AIC79XX
+ default y
+ help
+ Compile in aic79xx debugging code that can be useful in diagnosing
+ driver errors.
+
+config AIC79XX_DEBUG_MASK
+ int "Debug code enable mask (16383 for all debugging)"
+ depends on SCSI_AIC79XX
+ default "0"
+ help
+ Bit mask of debug options that is only valid if the
+ CONFIG_AIC79XX_DEBUG_ENBLE option is enabled. The bits in this mask
+ are defined in the drivers/scsi/aic7xxx/aic79xx.h - search for the
+ variable ahd_debug in that file to find them.
+
+config AIC79XX_REG_PRETTY_PRINT
+ bool "Decode registers during diagnostics"
+ depends on SCSI_AIC79XX
+ default y
+ help
+ Compile in register value tables for the output of expanded register
+ contents in diagnostics. This make it much easier to understand debug
+ output without having to refer to a data book and/or the aic7xxx.reg
+ file.
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic7xxx b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
new file mode 100644
index 000000000000..fc35ea3846ef
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
@@ -0,0 +1,100 @@
+#
+# AIC7XXX and AIC79XX 2.5.X Kernel configuration File.
+# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Kconfig.aic7xxx#4 $
+#
+config SCSI_AIC7XXX
+ tristate "Adaptec AIC7xxx Fast -> U160 support (New Driver)"
+ ---help---
+ This driver supports all of Adaptec's Fast through Ultra 160 PCI
+ based SCSI controllers as well as the aic7770 based EISA and VLB
+ SCSI controllers (the 274x and 284x series). For AAA and ARO based
+ configurations, only SCSI functionality is provided.
+
+ If you want to compile the driver as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read <file:Documentation/modules.txt>. The module
+ will be called aic7xxx.o.
+
+config AIC7XXX_CMDS_PER_DEVICE
+ int "Maximum number of TCQ commands per device"
+ depends on SCSI_AIC7XXX
+ default "32"
+ ---help---
+ Specify the number of commands you would like to allocate per SCSI
+ device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+ This is an upper bound value for the number of tagged transactions
+ to be used for any device. The aic7xxx driver will automatically
+ vary this number based on device behavior. For devices with a
+ fixed maximum, the driver will eventually lock to this maximum
+ and display a console message inidicating this value.
+
+ Due to resource allocation issues in the Linux SCSI mid-layer, using
+ a high number of commands per device may result in memory allocation
+ failures when many devices are attached to the system. For this reason,
+ the default is set to 32. Higher values may result in higer performance
+ on some devices. The upper bound is 253. 0 disables tagged queueing.
+
+ Per device tag depth can be controlled via the kernel command line
+ "tag_info" option. See drivers/scsi/aic7xxx/README.aic7xxx
+ for details.
+
+config AIC7XXX_RESET_DELAY_MS
+ int "Initial bus reset delay in milli-seconds"
+ depends on SCSI_AIC7XXX
+ default "15000"
+ ---help---
+ The number of milliseconds to delay after an initial bus reset.
+ The bus settle delay following all error recovery actions is
+ dictated by the SCSI layer and is not affected by this value.
+
+ Default: 15000 (15 seconds)
+
+config AIC7XXX_PROBE_EISA_VL
+ bool "Probe for EISA and VL AIC7XXX Adapters"
+ help
+ Probe for EISA and VLB Aic7xxx controllers. In many newer systems,
+ the invasive probes necessary to detect these controllers can cause
+ other devices to fail. For this reason, the non-PCI probe code is
+ disabled by default. The current value of this option can be "toggled"
+ via the no_probe kernel command line option.
+
+config AIC7XXX_BUILD_FIRMWARE
+ bool "Build Adapter Firmware with Kernel Build"
+ depends on SCSI_AIC7XXX
+ help
+ This option should only be enabled if you are modifying the firmware
+ source to the aic7xxx driver and wish to have the generated firmware
+ include files updated during a normal kernel build. The assembler
+ for the firmware requires lex and yacc or their equivalents, as well
+ as the db v1 library. You may have to install additional packages
+ or modify the assembler Makefile or the files it includes if your
+ build environment is different than that of the author.
+
+config AIC7XXX_DEBUG_ENABLE
+ bool "Compile in Debugging Code"
+ depends on SCSI_AIC7XXX
+ default y
+ help
+ Compile in aic7xxx debugging code that can be useful in diagnosing
+ driver errors.
+
+config AIC7XXX_DEBUG_MASK
+ int "Debug code enable mask (2047 for all debugging)"
+ depends on SCSI_AIC7XXX
+ default "0"
+ help
+ Bit mask of debug options that is only valid if the
+ CONFIG_AIC7XXX_DEBUG_ENBLE option is enabled. The bits in this mask
+ are defined in the drivers/scsi/aic7xxx/aic7xxx.h - search for the
+ variable ahc_debug in that file to find them.
+
+config AIC7XXX_REG_PRETTY_PRINT
+ bool "Decode registers during diagnostics"
+ depends on SCSI_AIC7XXX
+ default y
+ help
+ Compile in register value tables for the output of expanded register
+ contents in diagnostics. This make it much easier to understand debug
+ output without having to refer to a data book and/or the aic7xxx.reg
+ file.
diff --git a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile
index cf3a49b1ebba..ebd922a22c97 100644
--- a/drivers/scsi/aic7xxx/Makefile
+++ b/drivers/scsi/aic7xxx/Makefile
@@ -1,47 +1,83 @@
#
# Makefile for the Linux aic7xxx SCSI driver.
#
+# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Makefile#3 $
+#
+
# Let kbuild descend into aicasm when cleaning
subdir- += aicasm
obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx.o
+obj-$(CONFIG_SCSI_AIC79XX) += aic79xx.o
-# Core files
-aic7xxx-objs += aic7xxx_core.o aic7xxx_93cx6.o aic7770.o
+# Core Fast -> U160 files
+aic7xxx-y += aic7xxx_core.o \
+ aic7xxx_93cx6.o \
+ aic7770.o
+aic7xxx-$(CONFIG_PCI) += aic7xxx_pci.o
+aic7xxx-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT) += aic7xxx_reg_print.o
-# Platform Specific Files
-aic7xxx-objs += aic7xxx_linux.o aic7xxx_proc.o aic7770_linux.o
+# Platform Specific Fast -> U160 Files
+aic7xxx-y += aic7xxx_osm.o \
+ aic7xxx_proc.o \
+ aic7770_osm.o
+aic7xxx-$(CONFIG_PCI) += aic7xxx_osm_pci.o
-# PCI Specific Files
-ifeq ($(CONFIG_PCI),y)
- # Core PCI files
- aic7xxx-objs += aic7xxx_pci.o
- # Platform Specific PCI Files
- aic7xxx-objs += aic7xxx_linux_pci.o
-endif
+# Core U320 files
+aic79xx-y += aic79xx_core.o \
+ aic79xx_pci.o
+aic79xx-$(CONFIG_AIC79XX_REG_PRETTY_PRINT) += aic79xx_reg_print.o
+# Platform Specific U320 Files
+aic79xx-y += aic79xx_osm.o \
+ aic79xx_proc.o \
+ aic79xx_osm_pci.o
+
+EXTRA_CFLAGS += -Idrivers/scsi
#EXTRA_CFLAGS += -g
# Files generated that shall be removed upon make clean
-clean-files := aic7xxx_seq.h aic7xxx_reg.h
+clean-files := aic7xxx_seq.h aic7xxx_reg.h aic7xxx_reg_print.c
+clean-files += aic79xx_seq.h aic79xx_reg.h aic79xx_reg_print.c
# Dependencies for generated files need to be listed explicitly
$(obj)/aic7xxx_core.o: $(obj)/aic7xxx_seq.h
+$(obj)/aic79xx_core.o: $(obj)/aic79xx_seq.h
-$(addprefix $(obj)/,$(aic7xxx-objs)): $(obj)/aic7xxx_reg.h
+$(addprefix $(obj)/,$(aic7xxx-y)): $(obj)/aic7xxx_reg.h
+$(addprefix $(obj)/,$(aic79xx-y)): $(obj)/aic79xx_reg.h
ifeq ($(CONFIG_AIC7XXX_BUILD_FIRMWARE),y)
+aic7xxx_gen = $(obj)/aic7xxx_seq.h $(obj)/aic7xxx_reg.h
+ifeq ($(CONFIG_AIC7XXX_REG_PRETTY_PRINT),y)
+aic7xxx_gen += $(obj)/aic7xxx_reg_print.c
+aic7xxx_asm_cmd = $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic7xxx_reg.h \
+ -p $(obj)/aic7xxx_reg_print.c -i aic7xxx_osm.h \
+ -o $(obj)/aic7xxx_seq.h $(src)/aic7xxx.seq
+else
+aic7xxx_asm_cmd = $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic7xxx_reg.h \
+ -o $(obj)/aic7xxx_seq.h $(src)/aic7xxx.seq
+endif
-$(obj)/aic7xxx_seq.h: $(src)/aic7xxx.seq $(src)/aic7xxx.reg \
- $(obj)/aicasm/aicasm
- $(obj)/aicasm/aicasm -I$(obj) -r $(obj)/aic7xxx_reg.h \
- -o $(obj)/aic7xxx_seq.h $(src)/aic7xxx.seq
+$(aic7xxx_gen): $(src)/aic7xxx.seq $(src)/aic7xxx.reg $(obj)/aicasm/aicasm
+ $(aic7xxx_asm_cmd)
+endif
-$(obj)/aic7xxx_reg.h: $(obj)/aic7xxx_seq.h
+ifeq ($(CONFIG_AIC79XX_BUILD_FIRMWARE),y)
+aic79xx_gen = $(obj)/aic79xx_seq.h $(obj)/aic79xx_reg.h
+ifeq ($(CONFIG_AIC79XX_REG_PRETTY_PRINT),y)
+aic79xx_gen += $(obj)/aic79xx_reg_print.c
+aic79xx_asm_cmd = $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic79xx_reg.h \
+ -p $(obj)/aic79xx_reg_print.c -i aic79xx_osm.h \
+ -o $(obj)/aic79xx_seq.h $(src)/aic79xx.seq
+else
+aic79xx_asm_cmd = $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic79xx_reg.h \
+ -o $(obj)/aic79xx_seq.h $(src)/aic79xx.seq
+endif
+$(aic79xx_gen): $(src)/aic79xx.seq $(src)/aic79xx.reg $(obj)/aicasm/aicasm
+ $(aic79xx_asm_cmd)
+endif
$(obj)/aicasm/aicasm: $(src)/aicasm/*.[chyl]
$(MAKE) -C $(src)/aicasm
-
-endif
-
diff --git a/drivers/scsi/aic7xxx/aic7770.c b/drivers/scsi/aic7xxx/aic7770.c
index f32adfd9badb..5229eb2cdafa 100644
--- a/drivers/scsi/aic7xxx/aic7770.c
+++ b/drivers/scsi/aic7xxx/aic7770.c
@@ -37,22 +37,29 @@
* 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#14 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7770.c#27 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aic7770.c,v 1.1 2000/09/16 20:02:27 gibbs Exp $
+ * $FreeBSD$
*/
+#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*/
-#define ID_AIC_7782 0x04907782
+#define ID_OLV_274x 0x04907782 /* Olivetti OEM */
+#define ID_OLV_274xD 0x04907783 /* Olivetti OEM (Differential) */
-static void aha2840_load_seeprom(struct ahc_softc *ahc);
+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;
@@ -72,18 +79,23 @@ struct aic7770_identity aic7770_ident_table [] =
"Adaptec 284X SCSI adapter",
ahc_aic7770_VL_setup
},
- /* Generic chip probes for devices we don't know 'exactly' */
{
- ID_AIC7770,
+ ID_OLV_274x,
0xFFFFFFFF,
- "Adaptec aic7770 SCSI adapter",
+ "Adaptec (Olivetti OEM) 274X SCSI adapter",
ahc_aic7770_EISA_setup
},
{
- /* (Olivetti 2 channel EISA) */
- ID_AIC_7782,
+ ID_OLV_274xD,
0xFFFFFFFF,
- "Adaptec aic7782 SCSI adapter",
+ "Adaptec (Olivetti OEM) 274X Differential SCSI adapter",
+ ahc_aic7770_EISA_setup
+ },
+ /* Generic chip probes for devices we don't know 'exactly' */
+ {
+ ID_AIC7770,
+ 0xFFFFFFFF,
+ "Adaptec aic7770 SCSI adapter",
ahc_aic7770_EISA_setup
}
};
@@ -104,21 +116,32 @@ aic7770_find_device(uint32_t id)
}
int
-aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry)
+aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry, u_int io)
{
+ u_long l;
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);
+ 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);
@@ -176,21 +199,22 @@ aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry)
ahc->flags |= AHC_TERM_ENB_B;
}
}
- /*
- * We have no way to tell, so assume extended
- * translation is enabled.
- */
- ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_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:
{
- aha2840_load_seeprom(ahc);
+ 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
@@ -209,24 +233,22 @@ aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry)
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);
- error = aic7770_map_int(ahc, irq);
- if (error != 0)
- return (error);
-
/*
* Enable the board's BUS drivers
*/
ahc_outb(ahc, BCTL, ENABLE);
- /*
- * Allow interrupts.
- */
- ahc_intr_enable(ahc, TRUE);
+ ahc_list_unlock(&l);
return (0);
}
@@ -234,14 +256,13 @@ aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry)
/*
* Read the 284x SEEPROM.
*/
-static void
+static int
aha2840_load_seeprom(struct ahc_softc *ahc)
{
- struct seeprom_descriptor sd;
- struct seeprom_config sc;
- uint16_t checksum = 0;
- uint8_t scsi_conf;
- int have_seeprom;
+ 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;
@@ -254,23 +275,16 @@ aha2840_load_seeprom(struct ahc_softc *ahc)
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 = read_seeprom(&sd,
- (uint16_t *)&sc,
- /*start_addr*/0,
- sizeof(sc)/2);
+ have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)sc,
+ /*start_addr*/0, sizeof(sc)/2);
if (have_seeprom) {
- /* Check checksum */
- int i;
- int maxaddr = (sizeof(sc)/2) - 1;
- uint16_t *scarray = (uint16_t *)&sc;
-
- for (i = 0; i < maxaddr; i++)
- checksum = checksum + scarray[i];
- if (checksum != sc.checksum) {
+
+ if (ahc_verify_cksum(sc) == 0) {
if(bootverbose)
printf ("checksum error\n");
have_seeprom = 0;
@@ -288,41 +302,44 @@ aha2840_load_seeprom(struct ahc_softc *ahc)
* Put the data we've collected down into SRAM
* where ahc_init will find it.
*/
- int i;
- int max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8;
+ 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)
+ 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)
+ if (sc->device_flags[i] & CFWIDEB)
target_settings |= WIDEXFER;
- if (sc.device_flags[i] & CFDISC)
+ 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;
+ ahc->our_id = sc->brtime_id & CFSCSIID;
scsi_conf = (ahc->our_id & 0x7);
- if (sc.adapter_control & CFSPARITY)
+ if (sc->adapter_control & CFSPARITY)
scsi_conf |= ENSPCHK;
- if (sc.adapter_control & CFRESETB)
+ if (sc->adapter_control & CFRESETB)
scsi_conf |= RESET_SCSI;
- if (sc.bios_control & CF284XEXTEND)
+ 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)
+ if (sc->adapter_control & CF284XSTERM)
ahc->flags |= AHC_TERM_ENB_A;
}
+ return (have_seeprom);
}
static int
diff --git a/drivers/scsi/aic7xxx/aic7770_linux.c b/drivers/scsi/aic7xxx/aic7770_osm.c
index 795602c194ed..80d25528e95c 100644
--- a/drivers/scsi/aic7xxx/aic7770_linux.c
+++ b/drivers/scsi/aic7xxx/aic7770_osm.c
@@ -36,7 +36,7 @@
* 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_linux.c#9 $
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7770_osm.c#11 $
*/
#include "aic7xxx_osm.h"
@@ -55,9 +55,6 @@ aic7770_linux_probe(Scsi_Host_Template *template)
int eisaBase;
int found;
- if (aic7xxx_no_probe)
- return (0);
-
eisaBase = 0x1000 + AHC_EISA_SLOT_OFFSET;
found = 0;
for (slot = 1; slot < NUMSLOTS; eisaBase+=0x1000, slot++) {
@@ -103,10 +100,9 @@ aic7770_linux_probe(Scsi_Host_Template *template)
*/
break;
}
- ahc->tag = BUS_SPACE_PIO;
- ahc->bsh.ioport = eisaBase;
- error = aic7770_config(ahc, entry);
+ error = aic7770_config(ahc, entry, eisaBase);
if (error != 0) {
+ ahc->bsh.ioport = 0;
ahc_free(ahc);
continue;
}
@@ -120,18 +116,19 @@ aic7770_linux_probe(Scsi_Host_Template *template)
}
int
-aic7770_map_registers(struct ahc_softc *ahc)
+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(ahc->bsh.ioport, AHC_EISA_IOSIZE, "aic7xxx");
+ request_region(port, AHC_EISA_IOSIZE, "aic7xxx");
#else
- if (request_region(ahc->bsh.ioport, AHC_EISA_IOSIZE, "aic7xxx") == 0)
+ if (request_region(port, AHC_EISA_IOSIZE, "aic7xxx") == 0)
return (ENOMEM);
#endif
-
+ ahc->tag = BUS_SPACE_PIO;
+ ahc->bsh.ioport = port;
return (0);
}
@@ -145,9 +142,9 @@ aic7770_map_int(struct ahc_softc *ahc, u_int irq)
if ((ahc->flags & AHC_EDGE_INTERRUPT) == 0)
shared = SA_SHIRQ;
- ahc->platform_data->irq = irq;
- error = request_irq(ahc->platform_data->irq, ahc_linux_isr,
- shared, "aic7xxx", ahc);
+ error = request_irq(irq, ahc_linux_isr, shared, "aic7xxx", ahc);
+ if (error == 0)
+ ahc->platform_data->irq = irq;
return (-error);
}
diff --git a/drivers/scsi/aic7xxx/aic79xx.h b/drivers/scsi/aic7xxx/aic79xx.h
new file mode 100644
index 000000000000..64a97dc8f998
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.h
@@ -0,0 +1,1480 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2002 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.h#78 $
+ *
+ * $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 QOUTFIFO_ENTRY_VALID 0x8000
+#define QOUTFIFO_ENTRY_VALID_LE (ahd_htole16(0x8000))
+#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)))
+#ifdef AHD_DEBUG
+#define SCB_IS_SILENT(scb) \
+ ((ahd_debug & AHD_SHOW_MASKED_ERRORS) == 0 \
+ && (((scb)->flags & SCB_SILENT) != 0))
+#else
+#define SCB_IS_SILENT(scb) \
+ (((scb)->flags & SCB_SILENT) != 0)
+#endif
+/*
+ * TCLs have the following format: TTTTLLLLLLLL
+ */
+#define TCL_TARGET_OFFSET(tcl) \
+ ((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+ (tcl & (AHD_NUM_LUNS - 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
+
+#define AHD_BUILD_COL_IDX(target, lun) \
+ (((lun) << 4) | target)
+
+#define AHD_GET_SCB_COL_IDX(ahd, scb) \
+ ((SCB_GET_LUN(scb) << 4) | SCB_GET_TARGET(ahd, scb))
+
+#define AHD_SET_SCB_COL_IDX(scb, col_idx) \
+do { \
+ (scb)->hscb->scsiid = ((col_idx) << TID_SHIFT) & TID; \
+ (scb)->hscb->lun = ((col_idx) >> 4) & (AHD_NUM_LUNS_NONPKT-1); \
+} while (0)
+
+#define AHD_COPY_SCB_COL_IDX(dst, src) \
+do { \
+ dst->hscb->scsiid = src->hscb->scsiid; \
+ dst->hscb->lun = src->hscb->lun; \
+} while (0)
+
+#define AHD_NEVER_COL_IDX 0xFFFF
+
+/**************************** 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.
+ */
+#define AHD_MAX_QUEUE AHD_SCB_MAX
+
+/*
+ * 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 AHD_MAX_QUEUE
+#define AHD_QOUT_SIZE AHD_MAX_QUEUE
+
+#define AHD_QIN_WRAP(x) ((x) & (AHD_QIN_SIZE-1))
+/*
+ * The maximum amount of SCB storage we allocate in host memory.
+ */
+#define AHD_SCB_MAX_ALLOC AHD_MAX_QUEUE
+
+/*
+ * 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 25
+
+/******************* 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_AIC7901A = 0x0003,
+ 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/Channel Device */
+ AHD_TARGETMODE = 0x01000,/* Has tested target mode support */
+ AHD_MULTIROLE = 0x02000,/* Space for two roles at a time */
+ AHD_RTI = 0x04000,/* Retained Training Support */
+ AHD_NEW_IOCELL_OPTS = 0x08000,/* More Signal knobs in the IOCELL */
+ AHD_NEW_DFCNTRL_OPTS = 0x10000,/* SCSIENWRDIS bit */
+ 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,
+ /*
+ * Rev A hardware fails to update LAST/CURR/NEXTSCB
+ * correctly in certain packetized selection cases.
+ */
+ AHD_SENT_SCB_UPDATE_BUG = 0x0001,
+ /* The wrong SCB is accessed to check the abort pending bit. */
+ AHD_ABORT_LQI_BUG = 0x0002,
+ /* Packetized bitbucket crosses packet boundaries. */
+ AHD_PKT_BITBUCKET_BUG = 0x0004,
+ /* The selection timer runs twice as long as its setting. */
+ AHD_LONG_SETIMO_BUG = 0x0008,
+ /* The Non-LQ CRC error status is delayed until phase change. */
+ AHD_NLQICRC_DELAYED_BUG = 0x0010,
+ /* The chip must be reset for all outgoing bus resets. */
+ AHD_SCSIRST_BUG = 0x0020,
+ /* Some PCIX fields must be saved and restored across chip reset. */
+ AHD_PCIX_CHIPRST_BUG = 0x0040,
+ /* MMAPIO is not functional in PCI-X mode. */
+ AHD_PCIX_MMAPIO_BUG = 0x0080,
+ /* Bug workarounds that can be disabled on non-PCIX busses. */
+ AHD_PCIX_BUG_MASK = AHD_PCIX_CHIPRST_BUG
+ | AHD_PCIX_MMAPIO_BUG,
+ /*
+ * LQOSTOP0 status set even for forced selections with ATN
+ * to perform non-packetized message delivery.
+ */
+ AHD_LQO_ATNO_BUG = 0x0100,
+ /* FIFO auto-flush does not always trigger. */
+ AHD_AUTOFLUSH_BUG = 0x0200,
+ /* The CLRLQO registers are not self-clearing. */
+ AHD_CLRLQO_AUTOCLR_BUG = 0x0400,
+ /* The PACKETIZED status bit refers to the previous connection. */
+ AHD_PKTIZED_STATUS_BUG = 0x0800,
+ /* "Short Luns" are not placed into outgoing LQ packets correctly. */
+ AHD_PKT_LUN_BUG = 0x1000,
+ /*
+ * Only the FIFO allocated to the non-packetized connection may
+ * be in use during a non-packetzied connection.
+ */
+ AHD_NONPACKFIFO_BUG = 0x2000,
+ /*
+ * Writing to a DFF SCBPTR register may fail if concurent with
+ * a hardware write to the other DFF SCBPTR register. This is
+ * not currently a concern in our sequencer since all chips with
+ * this bug have the AHD_NONPACKFIFO_BUG and all writes of concern
+ * occur in non-packetized connections.
+ */
+ AHD_MDFF_WSCBPTR_BUG = 0x4000,
+ /* SGHADDR updates are slow. */
+ AHD_REG_SLOW_SETTLE_BUG = 0x8000,
+ /*
+ * Changing the MODE_PTR coincident with an interrupt that
+ * switches to a different mode will cause the interrupt to
+ * be in the mode written outside of interrupt context.
+ */
+ AHD_SET_MODE_BUG = 0x10000,
+ /* Non-packetized busfree revision does not work. */
+ AHD_BUSFREEREV_BUG = 0x20000,
+ /*
+ * Paced transfers are indicated with a non-standard PPR
+ * option bit in the neg table, 160MHz is indicated by
+ * sync factor 0x7, and the offset if off by a factor of 2.
+ */
+ AHD_PACED_NEGTABLE_BUG = 0x40000,
+ /* LQOOVERRUN false positives. */
+ AHD_LQOOVERRUN_BUG = 0x80000,
+ /*
+ * Controller write to INTSTAT will lose to a host
+ * write to CLRINT.
+ */
+ AHD_INTCOLLISION_BUG = 0x100000
+} 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_HP_BOARD = 0x100000,
+ AHD_RESET_POLL_ACTIVE = 0x200000,
+ AHD_UPDATE_PEND_CMDS = 0x400000,
+ AHD_RUNNING_QOUTFIFO = 0x800000
+} 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*/ uint16_t tag;
+/*18*/ uint8_t cdb_len;
+/*19*/ uint8_t task_management;
+/*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;
+/*44*/ uint32_t hscb_busaddr;
+/******* Long lun field only downloaded for full 8 byte lun support *******/
+/*48*/ uint8_t pkt_long_lun[8];
+/******* Fields below are not Downloaded (Sequencer may use for scratch) ******/
+/*56*/ uint8_t spare[8];
+};
+
+/************************ 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_FLAG_NONE = 0x00000,
+ SCB_TRANSMISSION_ERROR = 0x00001,/*
+ * 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 = 0x00002,/*
+ * 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 = 0x00004,
+ SCB_SENSE = 0x00008,
+ SCB_CDB32_PTR = 0x00010,
+ SCB_RECOVERY_SCB = 0x00020,
+ SCB_AUTO_NEGOTIATE = 0x00040,/* Negotiate to achieve goal. */
+ SCB_NEGOTIATE = 0x00080,/* Negotiation forced for command. */
+ SCB_ABORT = 0x00100,
+ SCB_ACTIVE = 0x00200,
+ SCB_TARGET_IMMEDIATE = 0x00400,
+ SCB_PACKETIZED = 0x00800,
+ SCB_EXPECT_PPR_BUSFREE = 0x01000,
+ SCB_PKT_SENSE = 0x02000,
+ SCB_CMDPHASE_ABORT = 0x04000,
+ SCB_ON_COL_LIST = 0x08000,
+ SCB_SILENT = 0x10000 /*
+ * Be quiet about transmission type
+ * errors. They are expected and we
+ * don't want to upset the user. This
+ * flag is typically used during DV.
+ */
+} scb_flag;
+
+struct scb {
+ struct hardware_scb *hscb;
+ union {
+ SLIST_ENTRY(scb) sle;
+ LIST_ENTRY(scb) le;
+ TAILQ_ENTRY(scb) tqe;
+ } links;
+ union {
+ SLIST_ENTRY(scb) sle;
+ LIST_ENTRY(scb) le;
+ TAILQ_ENTRY(scb) tqe;
+ } links2;
+#define pending_links links2.le
+#define collision_links links2.le
+ struct scb *col_scb;
+ 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 */
+#define AHD_MAX_LQ_CRC_ERRORS 5
+ u_int crc_retry_count;
+};
+
+TAILQ_HEAD(scb_tailq, scb);
+LIST_HEAD(scb_list, scb);
+
+struct scb_data {
+ /*
+ * TAILQ of lists of free SCBs grouped by device
+ * collision domains.
+ */
+ struct scb_tailq free_scbs;
+
+ /*
+ * Per-device lists of SCBs whose tag ID would collide
+ * with an already active tag on the device.
+ */
+ struct scb_list free_scb_lists[AHD_NUM_TARGETS * AHD_NUM_LUNS_NONPKT];
+
+ /*
+ * SCBs that will not collide with any active device.
+ */
+ struct scb_list any_dev_free_scb_list;
+
+ /*
+ * Mapping from tag to SCB.
+ */
+ struct scb *scbindex[AHD_SCB_MAX];
+
+ /*
+ * "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_10MHz 0x19
+
+#define AHD_WIDTH_UNKNOWN 0xFF
+#define AHD_PERIOD_UNKNOWN 0xFF
+#define AHD_OFFSET_UNKNOWN 0x0
+#define AHD_PPR_OPTS_UNKNOWN 0xFF
+
+/*
+ * 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_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
+#define AHD_SYNCRATE_MAX AHD_SYNCRATE_160
+
+/* Safe and valid period for async negotiations. */
+#define AHD_ASYNC_XFER_PERIOD 0x44
+
+/*
+ * 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_EXPECT_QASREJ_BUSFREE = 0x08,
+ MSG_FLAG_PACKETIZED = 0x10
+} 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 *********************/
+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 hardware_scb *next_queued_hscb;
+
+ /*
+ * 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;
+
+ /*
+ * 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;
+ ahd_timer_t stat_timer;
+
+ /*
+ * Statistics.
+ */
+#define AHD_STAT_UPDATE_US 250000 /* 250ms */
+#define AHD_STAT_BUCKETS 4
+ u_int cmdcmplt_bucket;
+ uint32_t cmdcmplt_counts[AHD_STAT_BUCKETS];
+ uint32_t cmdcmplt_total;
+
+ /*
+ * 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 qoutfifonext_valid_tag;
+ 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;
+
+ /*
+ * Target incoming command FIFO.
+ */
+ struct target_cmd *targetcmds;
+ uint8_t tqinfifonext;
+
+ /*
+ * Cached verson of the hs_mailbox so we can avoid
+ * pausing the sequencer during mailbox updates.
+ */
+ uint8_t hs_mailbox;
+
+ /*
+ * 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;
+
+ /* IO Cell Parameters */
+ uint8_t iocell_opts[AHD_NUM_PER_DEV_ANNEXCOLS];
+
+ u_int stack_size;
+ uint16_t *saved_stack;
+
+ /* Per-Unit descriptive information */
+ const char *description;
+ const char *bus_description;
+ char *name;
+ int unit;
+
+ /* Selection Timer settings */
+ int seltime;
+
+ /*
+ * Interrupt coalessing settings.
+ */
+#define AHD_INT_COALESSING_TIMER_DEFAULT 250 /*us*/
+#define AHD_INT_COALESSING_MAXCMDS_DEFAULT 10
+#define AHD_INT_COALESSING_MAXCMDS_MAX 127
+#define AHD_INT_COALESSING_MINCMDS_DEFAULT 5
+#define AHD_INT_COALESSING_MINCMDS_MAX 127
+#define AHD_INT_COALESSING_THRESHOLD_DEFAULT 2000
+#define AHD_INT_COALESSING_STOP_THRESHOLD_DEFAULT 1000
+ u_int int_coalessing_timer;
+ u_int int_coalessing_maxcmds;
+ u_int int_coalessing_mincmds;
+ u_int int_coalessing_threshold;
+ u_int int_coalessing_stop_threshold;
+
+ 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;
+
+/*************************** IO Cell Configuration ****************************/
+#define AHD_PRECOMP_SLEW_INDEX \
+ (AHD_ANNEXCOL_PRECOMP_SLEW - AHD_ANNEXCOL_PER_DEV0)
+
+#define AHD_AMPLITUDE_INDEX \
+ (AHD_ANNEXCOL_AMPLITUDE - AHD_ANNEXCOL_PER_DEV0)
+
+#define AHD_SET_SLEWRATE(ahd, new_slew) \
+do { \
+ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_SLEWRATE_MASK; \
+ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] |= \
+ (((new_slew) << AHD_SLEWRATE_SHIFT) & AHD_SLEWRATE_MASK); \
+} while (0)
+
+#define AHD_SET_PRECOMP(ahd, new_pcomp) \
+do { \
+ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_PRECOMP_MASK; \
+ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] |= \
+ (((new_pcomp) << AHD_PRECOMP_SHIFT) & AHD_PRECOMP_MASK); \
+} while (0)
+
+#define AHD_SET_AMPLITUDE(ahd, new_amp) \
+do { \
+ (ahd)->iocell_opts[AHD_AMPLITUDE_INDEX] &= ~AHD_AMPLITUDE_MASK; \
+ (ahd)->iocell_opts[AHD_AMPLITUDE_INDEX] |= \
+ (((new_amp) << AHD_AMPLITUDE_SHIFT) & AHD_AMPLITUDE_MASK); \
+} while (0)
+
+/************************ 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 ****************************/
+/******************************************************************************/
+void ahd_reset_cmds_pending(struct ahd_softc *ahd);
+u_int ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl);
+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 *);
+int ahd_pci_test_register_access(struct ahd_softc *);
+
+/************************** SCB and SCB queue management **********************/
+int ahd_probe_scbs(struct ahd_softc *);
+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_update_coalessing_values(struct ahd_softc *ahd,
+ u_int timer,
+ u_int maxcmds,
+ u_int mincmds);
+void ahd_enable_coalessing(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 *);
+struct scb *ahd_get_scb(struct ahd_softc *ahd, u_int col_idx);
+void ahd_free_scb(struct ahd_softc *ahd, struct scb *scb);
+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_flush_qoutfifo(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);
+/*
+ * Negotiation types. These are used to qualify if we should renegotiate
+ * even if our goal and current transport parameters are identical.
+ */
+typedef enum {
+ AHD_NEG_TO_GOAL, /* Renegotiate only if goal and curr differ. */
+ AHD_NEG_IF_NON_ASYNC, /* Renegotiate so long as goal is non-async. */
+ AHD_NEG_ALWAYS /* Renegotiat even if goal is async. */
+} ahd_neg_type;
+int ahd_update_neg_request(struct ahd_softc*,
+ struct ahd_devinfo*,
+ struct ahd_tmode_tstate*,
+ struct ahd_initiator_tinfo*,
+ ahd_neg_type);
+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 0x00001
+#define AHD_SHOW_SENSE 0x00002
+#define AHD_SHOW_RECOVERY 0x00004
+#define AHD_DUMP_SEEPROM 0x00008
+#define AHD_SHOW_TERMCTL 0x00010
+#define AHD_SHOW_MEMORY 0x00020
+#define AHD_SHOW_MESSAGES 0x00040
+#define AHD_SHOW_MODEPTR 0x00080
+#define AHD_SHOW_SELTO 0x00100
+#define AHD_SHOW_FIFOS 0x00200
+#define AHD_SHOW_QFULL 0x00400
+#define AHD_SHOW_DV 0x00800
+#define AHD_SHOW_MASKED_ERRORS 0x01000
+#define AHD_SHOW_QUEUE 0x02000
+#define AHD_SHOW_TQIN 0x04000
+#define AHD_SHOW_SG 0x08000
+#define AHD_SHOW_INT_COALESSING 0x10000
+#define AHD_DEBUG_SEQUENCER 0x20000
+#endif
+void ahd_print_scb(struct scb *scb);
+void ahd_print_devinfo(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+void ahd_dump_sglist(struct scb *scb);
+void ahd_dump_all_cards_state(void);
+void ahd_dump_card_state(struct ahd_softc *ahd);
+int ahd_print_register(ahd_reg_parse_entry_t *table,
+ u_int num_entries,
+ const char *name,
+ u_int address,
+ u_int value,
+ u_int *cur_column,
+ u_int wrap_point);
+void ahd_dump_scbs(struct ahd_softc *ahd);
+#endif /* _AIC79XX_H_ */
diff --git a/drivers/scsi/aic7xxx/aic79xx.reg b/drivers/scsi/aic7xxx/aic79xx.reg
new file mode 100644
index 000000000000..439e81379ac8
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.reg
@@ -0,0 +1,3933 @@
+/*
+ * Aic79xx register and scratch ram definitions.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#59 $"
+
+/*
+ * 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; \
+ if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
+ mvi MK_MODE(src, dst) call set_mode_work_around; \
+ } else { \
+ mvi MODE_PTR, MK_MODE(src, dst); \
+ }
+
+#define TOGGLE_DFF_MODE \
+ if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
+ call toggle_dff_mode_work_around; \
+ } else { \
+ xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1); \
+ }
+
+#define RESTORE_MODE(mode) \
+ if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
+ mov mode call set_mode_work_around; \
+ } else { \
+ mov MODE_PTR, mode; \
+ }
+
+#define SET_SEQINTCODE(code) \
+ if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) { \
+ mvi code call set_seqint_work_around; \
+ } else { \
+ mvi SEQINTCODE, code; \
+ }
+
+/*
+ * 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
+ field DST_MODE 0x70
+ field 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
+ field HWERRINT 0x80
+ field BRKADRINT 0x40
+ field SWTMINT 0x20
+ field PCIINT 0x10
+ field SCSIINT 0x08
+ field SEQINT 0x04
+ field CMDCMPLT 0x02
+ field SPLTINT 0x01
+ mask INT_PEND 0xFF
+}
+
+/*
+ * Sequencer Interrupt Code
+ */
+register SEQINTCODE {
+ address 0x002
+ access_mode RW
+ field {
+ NO_SEQINT, /* No seqint pending. */
+ BAD_PHASE, /* unknown scsi bus phase */
+ SEND_REJECT, /* sending a message reject */
+ PROTO_VIOLATION, /* Protocol Violation */
+ NO_MATCH, /* no cmd match for reconnect */
+ IGN_WIDE_RES, /* Complex IGN Wide Res Msg */
+ PDATA_REINIT, /*
+ * Returned to data phase
+ * that requires data
+ * transfer pointers to be
+ * recalculated from the
+ * transfer residual.
+ */
+ HOST_MSG_LOOP, /*
+ * 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.
+ */
+ BAD_STATUS, /* Bad status from target */
+ DATA_OVERRUN, /*
+ * Target attempted to write
+ * beyond the bounds of its
+ * command.
+ */
+ MKMSG_FAILED, /*
+ * Target completed command
+ * without honoring our ATN
+ * request to issue a message.
+ */
+ MISSED_BUSFREE, /*
+ * The sequencer never saw
+ * the bus go free after
+ * either a command complete
+ * or disconnect message.
+ */
+ DUMP_CARD_STATE,
+ ILLEGAL_PHASE,
+ INVALID_SEQINT,
+ CFG4ISTAT_INTR,
+ STATUS_OVERRUN,
+ CFG4OVERRUN,
+ ENTERING_NONPACK,
+ TRACEPOINT0,
+ TRACEPOINT1,
+ TRACEPOINT2,
+ TRACEPOINT3,
+ SAW_HWERR
+ }
+}
+
+/*
+ * Clear Host Interrupt
+ */
+register CLRINT {
+ address 0x003
+ access_mode WO
+ field CLRHWERRINT 0x80 /* Rev B or greater */
+ field CLRBRKADRINT 0x40
+ field CLRSWTMINT 0x20
+ field CLRPCIINT 0x10
+ field CLRSCSIINT 0x08
+ field CLRSEQINT 0x04
+ field CLRCMDINT 0x02
+ field CLRSPLTINT 0x01
+}
+
+/*
+ * Error Register
+ */
+register ERROR {
+ address 0x004
+ access_mode RO
+ field CIOPARERR 0x80
+ field CIOACCESFAIL 0x40 /* Rev B or greater */
+ field MPARERR 0x20
+ field DPARERR 0x10
+ field SQPARERR 0x08
+ field ILLOPCODE 0x04
+ field DSCTMOUT 0x02
+}
+
+/*
+ * Clear Error
+ */
+register CLRERR {
+ address 0x004
+ access_mode WO
+ field CLRCIOPARERR 0x80
+ field CLRCIOACCESFAIL 0x40 /* Rev B or greater */
+ field CLRMPARERR 0x20
+ field CLRDPARERR 0x10
+ field CLRSQPARERR 0x08
+ field CLRILLOPCODE 0x04
+ field CLRDSCTMOUT 0x02
+}
+
+/*
+ * Host Control Register
+ * Overall host control of the device.
+ */
+register HCNTRL {
+ address 0x005
+ access_mode RW
+ field SEQ_RESET 0x80 /* Rev B or greater */
+ field POWRDN 0x40
+ field SWINT 0x10
+ field SWTIMER_START_B 0x08 /* Rev B or greater */
+ field PAUSE 0x04
+ field INTEN 0x02
+ field CHIPRST 0x01
+ field 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 0x00B
+ access_mode RW
+ mask HOST_TQINPOS 0x80 /* Boundary at either 0 or 128 */
+ mask ENINT_COALESS 0x40 /* Perform interrupt coalessing */
+}
+
+/*
+ * Sequencer Interupt Status
+ */
+register SEQINTSTAT {
+ address 0x00C
+ access_mode RO
+ field SEQ_SWTMRTO 0x10
+ field SEQ_SEQINT 0x08
+ field SEQ_SCSIINT 0x04
+ field SEQ_PCIINT 0x02
+ field SEQ_SPLTINT 0x01
+}
+
+/*
+ * Clear SEQ Interrupt
+ */
+register CLRSEQINTSTAT {
+ address 0x00C
+ access_mode WO
+ field CLRSEQ_SWTMRTO 0x10
+ field CLRSEQ_SEQINT 0x08
+ field CLRSEQ_SCSIINT 0x04
+ field CLRSEQ_PCIINT 0x02
+ field CLRSEQ_SPLTINT 0x01
+}
+
+/*
+ * Software Timer
+ */
+register SWTIMER {
+ address 0x00E
+ 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
+ field EMPTY_SCB_AVAIL 0x80
+ field NEW_SCB_AVAIL 0x40
+ field SDSCB_ROLLOVR 0x20
+ field HS_MAILBOX_ACT 0x10
+ field SCB_QSIZE 0x0F {
+ SCB_QSIZE_4,
+ SCB_QSIZE_8,
+ SCB_QSIZE_16,
+ SCB_QSIZE_32,
+ SCB_QSIZE_64,
+ SCB_QSIZE_128,
+ SCB_QSIZE_256,
+ SCB_QSIZE_512,
+ SCB_QSIZE_1024,
+ SCB_QSIZE_2048,
+ SCB_QSIZE_4096,
+ SCB_QSIZE_8192,
+ SCB_QSIZE_16384
+ }
+}
+
+/*
+ * Interrupt Control
+ */
+register INTCTL {
+ address 0x018
+ access_mode RW
+ field SWTMINTMASK 0x80
+ field SWTMINTEN 0x40
+ field SWTIMER_START 0x20
+ field AUTOCLRCMDINT 0x10
+ field PCIINTEN 0x08
+ field SCSIINTEN 0x04
+ field SEQINTEN 0x02
+ field SPLTINTEN 0x01
+}
+
+/*
+ * Data FIFO Control
+ */
+register DFCNTRL {
+ address 0x019
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field PRELOADEN 0x80
+ field SCSIENWRDIS 0x40 /* Rev B only. */
+ field SCSIEN 0x20
+ field SCSIENACK 0x20
+ field HDMAEN 0x08
+ field HDMAENACK 0x08
+ field DIRECTION 0x04
+ field DIRECTIONACK 0x04
+ field FIFOFLUSH 0x02
+ field FIFOFLUSHACK 0x02
+ field DIRECTIONEN 0x01
+}
+
+/*
+ * Device Space Command 0
+ */
+register DSCOMMAND0 {
+ address 0x019
+ access_mode RW
+ modes M_CFG
+ field CACHETHEN 0x80 /* Cache Threshold enable */
+ field DPARCKEN 0x40 /* Data Parity Check Enable */
+ field MPARCKEN 0x20 /* Memory Parity Check Enable */
+ field EXTREQLCK 0x10 /* External Request Lock */
+ field DISABLE_TWATE 0x02 /* Rev B or greater */
+ field CIOPARCKEN 0x01 /* Internal bus parity error enable */
+}
+
+/*
+ * Data FIFO Status
+ */
+register DFSTATUS {
+ address 0x01A
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field PRELOAD_AVAIL 0x80
+ field PKT_PRELOAD_AVAIL 0x40
+ field MREQPEND 0x10
+ field HDONE 0x08
+ field DFTHRESH 0x04
+ field FIFOFULL 0x02
+ field FIFOEMP 0x01
+}
+
+/*
+ * S/G Cache Pointer
+ */
+register SG_CACHE_PRE {
+ address 0x01B
+ access_mode WO
+ modes M_DFF0, M_DFF1
+ field SG_ADDR_MASK 0xf8
+ field ODD_SEG 0x04
+ field LAST_SEG 0x02
+}
+
+register SG_CACHE_SHADOW {
+ address 0x01B
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field SG_ADDR_MASK 0xf8
+ field ODD_SEG 0x04
+ field LAST_SEG 0x02
+ field LAST_SEG_DONE 0x01
+}
+
+/*
+ * Arbiter Control
+ */
+register ARBCTL {
+ address 0x01B
+ access_mode RW
+ modes M_CFG
+ field RESET_HARB 0x80
+ field RETRY_SWEN 0x08
+ field 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
+}
+
+/*
+ * PCI PLL Delay.
+ */
+register PLLDELAY {
+ address 0x070
+ access_mode RW
+ size 1
+ modes M_CFG
+ field SPLIT_DROP_REQ 0x80
+}
+
+/*
+ * 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
+ field WR_DFTHRSH 0x70 {
+ WR_DFTHRSH_MIN,
+ WR_DFTHRSH_25,
+ WR_DFTHRSH_50,
+ WR_DFTHRSH_63,
+ WR_DFTHRSH_75,
+ WR_DFTHRSH_85,
+ WR_DFTHRSH_90,
+ WR_DFTHRSH_MAX
+ }
+ field RD_DFTHRSH 0x07 {
+ RD_DFTHRSH_MIN,
+ RD_DFTHRSH_25,
+ RD_DFTHRSH_50,
+ RD_DFTHRSH_63,
+ RD_DFTHRSH_75,
+ RD_DFTHRSH_85,
+ RD_DFTHRSH_90,
+ RD_DFTHRSH_MAX
+ }
+}
+
+/*
+ * ROM Address
+ */
+register ROMADDR {
+ address 0x08A
+ access_mode RW
+ size 3
+}
+
+/*
+ * ROM Control
+ */
+register ROMCNTRL {
+ address 0x08D
+ access_mode RW
+ field ROMOP 0xE0
+ field ROMSPD 0x18
+ field REPEAT 0x02
+ field 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
+ field CDNUM 0xF8
+ field CFNUM 0x07
+}
+
+/*
+ * CMC Recieve Message 0
+ */
+register CMCRXMSG0 {
+ address 0x090
+ access_mode RO
+ modes M_CCHAN
+ field CDNUM 0xF8
+ field CFNUM 0x07
+}
+
+/*
+ * Overlay Recieve Message 0
+ */
+register OVLYRXMSG0 {
+ address 0x090
+ access_mode RO
+ modes M_SCSI
+ field CDNUM 0xF8
+ field CFNUM 0x07
+}
+
+/*
+ * Relaxed Order Enable
+ */
+register ROENABLE {
+ address 0x090
+ access_mode RW
+ modes M_CFG
+ field MSIROEN 0x20
+ field OVLYROEN 0x10
+ field CMCROEN 0x08
+ field SGROEN 0x04
+ field DCH1ROEN 0x02
+ field DCH0ROEN 0x01
+}
+
+/*
+ * Data Channel Receive Message 1
+ */
+register DCHRXMSG1 {
+ address 0x091
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field CBNUM 0xFF
+}
+
+/*
+ * CMC Recieve Message 1
+ */
+register CMCRXMSG1 {
+ address 0x091
+ access_mode RO
+ modes M_CCHAN
+ field CBNUM 0xFF
+}
+
+/*
+ * Overlay Recieve Message 1
+ */
+register OVLYRXMSG1 {
+ address 0x091
+ access_mode RO
+ modes M_SCSI
+ field CBNUM 0xFF
+}
+
+/*
+ * No Snoop Enable
+ */
+register NSENABLE {
+ address 0x091
+ access_mode RW
+ modes M_CFG
+ field MSINSEN 0x20
+ field OVLYNSEN 0x10
+ field CMCNSEN 0x08
+ field SGNSEN 0x04
+ field DCH1NSEN 0x02
+ field DCH0NSEN 0x01
+}
+
+/*
+ * Data Channel Receive Message 2
+ */
+register DCHRXMSG2 {
+ address 0x092
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field MINDEX 0xFF
+}
+
+/*
+ * CMC Recieve Message 2
+ */
+register CMCRXMSG2 {
+ address 0x092
+ access_mode RO
+ modes M_CCHAN
+ field MINDEX 0xFF
+}
+
+/*
+ * Overlay Recieve Message 2
+ */
+register OVLYRXMSG2 {
+ address 0x092
+ access_mode RO
+ modes M_SCSI
+ field 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
+ field MCLASS 0x0F
+}
+
+/*
+ * CMC Recieve Message 3
+ */
+register CMCRXMSG3 {
+ address 0x093
+ access_mode RO
+ modes M_CCHAN
+ field MCLASS 0x0F
+}
+
+/*
+ * Overlay Recieve Message 3
+ */
+register OVLYRXMSG3 {
+ address 0x093
+ access_mode RO
+ modes M_SCSI
+ field MCLASS 0x0F
+}
+
+/*
+ * PCI-X Control
+ */
+register PCIXCTL {
+ address 0x093
+ access_mode RW
+ modes M_CFG
+ field SERRPULSE 0x80
+ field UNEXPSCIEN 0x20
+ field SPLTSMADIS 0x10
+ field SPLTSTADIS 0x08
+ field SRSPDPEEN 0x04
+ field TSCSERREN 0x02
+ field 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
+ field STAETERM 0x80
+ field SCBCERR 0x40
+ field SCADERR 0x20
+ field SCDATBUCKET 0x10
+ field CNTNOTCMPLT 0x08
+ field RXOVRUN 0x04
+ field RXSCEMSG 0x02
+ field RXSPLTRSP 0x01
+}
+
+/*
+ * CMC Split Status 0
+ */
+register CMCSPLTSTAT0 {
+ address 0x096
+ access_mode RW
+ modes M_CCHAN
+ field STAETERM 0x80
+ field SCBCERR 0x40
+ field SCADERR 0x20
+ field SCDATBUCKET 0x10
+ field CNTNOTCMPLT 0x08
+ field RXOVRUN 0x04
+ field RXSCEMSG 0x02
+ field RXSPLTRSP 0x01
+}
+
+/*
+ * Overlay Split Status 0
+ */
+register OVLYSPLTSTAT0 {
+ address 0x096
+ access_mode RW
+ modes M_SCSI
+ field STAETERM 0x80
+ field SCBCERR 0x40
+ field SCADERR 0x20
+ field SCDATBUCKET 0x10
+ field CNTNOTCMPLT 0x08
+ field RXOVRUN 0x04
+ field RXSCEMSG 0x02
+ field RXSPLTRSP 0x01
+}
+
+/*
+ * Data Channel Split Status 1
+ */
+register DCHSPLTSTAT1 {
+ address 0x097
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field RXDATABUCKET 0x01
+}
+
+/*
+ * CMC Split Status 1
+ */
+register CMCSPLTSTAT1 {
+ address 0x097
+ access_mode RW
+ modes M_CCHAN
+ field RXDATABUCKET 0x01
+}
+
+/*
+ * Overlay Split Status 1
+ */
+register OVLYSPLTSTAT1 {
+ address 0x097
+ access_mode RW
+ modes M_SCSI
+ field RXDATABUCKET 0x01
+}
+
+/*
+ * S/G Receive Message 0
+ */
+register SGRXMSG0 {
+ address 0x098
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field CDNUM 0xF8
+ field CFNUM 0x07
+}
+
+/*
+ * S/G Receive Message 1
+ */
+register SGRXMSG1 {
+ address 0x099
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field CBNUM 0xFF
+}
+
+/*
+ * S/G Receive Message 2
+ */
+register SGRXMSG2 {
+ address 0x09A
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field MINDEX 0xFF
+}
+
+/*
+ * S/G Receive Message 3
+ */
+register SGRXMSG3 {
+ address 0x09B
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field MCLASS 0x0F
+}
+
+/*
+ * Slave Split Out Address 0
+ */
+register SLVSPLTOUTADR0 {
+ address 0x098
+ access_mode RO
+ modes M_SCSI
+ field LOWER_ADDR 0x7F
+}
+
+/*
+ * Slave Split Out Address 1
+ */
+register SLVSPLTOUTADR1 {
+ address 0x099
+ access_mode RO
+ modes M_SCSI
+ field REQ_DNUM 0xF8
+ field REQ_FNUM 0x07
+}
+
+/*
+ * Slave Split Out Address 2
+ */
+register SLVSPLTOUTADR2 {
+ address 0x09A
+ access_mode RO
+ modes M_SCSI
+ field REQ_BNUM 0xFF
+}
+
+/*
+ * Slave Split Out Address 3
+ */
+register SLVSPLTOUTADR3 {
+ address 0x09B
+ access_mode RO
+ modes M_SCSI
+ field RLXORD 020
+ field 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
+ field LOWER_BCNT 0xFF
+}
+
+/*
+ * Slave Split Out Attribute 1
+ */
+register SLVSPLTOUTATTR1 {
+ address 0x09D
+ access_mode RO
+ modes M_SCSI
+ field CMPLT_DNUM 0xF8
+ field CMPLT_FNUM 0x07
+}
+
+/*
+ * Slave Split Out Attribute 2
+ */
+register SLVSPLTOUTATTR2 {
+ address 0x09E
+ access_mode RO
+ size 2
+ modes M_SCSI
+ field CMPLT_BNUM 0xFF
+}
+/*
+ * S/G Split Status 0
+ */
+register SGSPLTSTAT0 {
+ address 0x09E
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field STAETERM 0x80
+ field SCBCERR 0x40
+ field SCADERR 0x20
+ field SCDATBUCKET 0x10
+ field CNTNOTCMPLT 0x08
+ field RXOVRUN 0x04
+ field RXSCEMSG 0x02
+ field RXSPLTRSP 0x01
+}
+
+/*
+ * S/G Split Status 1
+ */
+register SGSPLTSTAT1 {
+ address 0x09F
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field RXDATABUCKET 0x01
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+ address 0x09f
+ access_mode RW
+ modes M_CFG
+ field TEST_GROUP 0xF0
+ field TEST_NUM 0x0F
+}
+
+/*
+ * Data FIFO 0 PCI Status
+ */
+register DF0PCISTAT {
+ address 0x0A0
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field TWATERR 0x02
+ field DPR 0x01
+}
+
+/*
+ * Data FIFO 1 PCI Status
+ */
+register DF1PCISTAT {
+ address 0x0A1
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field TWATERR 0x02
+ field DPR 0x01
+}
+
+/*
+ * S/G PCI Status
+ */
+register SGPCISTAT {
+ address 0x0A2
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field DPR 0x01
+}
+
+/*
+ * CMC PCI Status
+ */
+register CMCPCISTAT {
+ address 0x0A3
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field TWATERR 0x02
+ field DPR 0x01
+}
+
+/*
+ * Overlay PCI Status
+ */
+register OVLYPCISTAT {
+ address 0x0A4
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field DPR 0x01
+}
+
+/*
+ * PCI Status for MSI Master DMA Transfer
+ */
+register MSIPCISTAT {
+ address 0x0A6
+ access_mode RW
+ modes M_CFG
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field CLRPENDMSI 0x08
+ field TWATERR 0x02
+ field DPR 0x01
+}
+
+/*
+ * PCI Status for Target
+ */
+register TARGPCISTAT {
+ address 0x0A7
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field STA 0x08
+ field 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
+}
+
+/*
+ * Rev B or greater.
+ */
+register MAXCMDBYTES {
+ address 0x02D
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Rev B or greater.
+ */
+register MAXCMD2RCV {
+ address 0x02E
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Rev B or greater.
+ */
+register SHORTTHRESH {
+ address 0x02F
+ 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
+ field LQITARGCLT 0xC0
+ field LQIINITGCLT 0x30
+ field LQ0TARGCLT 0x0C
+ field LQ0INITGCLT 0x03
+}
+
+/*
+ * LQ Manager Control 1
+ */
+register LQCTL1 {
+ address 0x038
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field PCI2PCI 0x04
+ field SINGLECMD 0x02
+ field ABORTPENDING 0x01
+}
+
+/*
+ * LQ Manager Control 2
+ */
+register LQCTL2 {
+ address 0x039
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQIRETRY 0x80
+ field LQICONTINUE 0x40
+ field LQITOIDLE 0x20
+ field LQIPAUSE 0x10
+ field LQORETRY 0x08
+ field LQOCONTINUE 0x04
+ field LQOTOIDLE 0x02
+ field LQOPAUSE 0x01
+}
+
+/*
+ * SCSI RAM BIST0
+ */
+register SCSBIST0 {
+ address 0x039
+ access_mode RW
+ modes M_CFG
+ field GSBISTERR 0x40
+ field GSBISTDONE 0x20
+ field GSBISTRUN 0x10
+ field OSBISTERR 0x04
+ field OSBISTDONE 0x02
+ field OSBISTRUN 0x01
+}
+
+/*
+ * SCSI Sequence Control0
+ */
+register SCSISEQ0 {
+ address 0x03A
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field TEMODEO 0x80
+ field ENSELO 0x40
+ field ENARBO 0x20
+ field FORCEBUSFREE 0x10
+ field SCSIRSTO 0x01
+}
+
+/*
+ * SCSI RAM BIST 1
+ */
+register SCSBIST1 {
+ address 0x03A
+ access_mode RW
+ modes M_CFG
+ field NTBISTERR 0x04
+ field NTBISTDONE 0x02
+ field NTBISTRUN 0x01
+}
+
+/*
+ * SCSI Sequence Control 1
+ */
+register SCSISEQ1 {
+ address 0x03B
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field MANUALCTL 0x40
+ field ENSELI 0x20
+ field ENRSELI 0x10
+ field MANUALP 0x0C
+ field ENAUTOATNP 0x02
+ field ALTSTIM 0x01
+}
+
+/*
+ * SCSI Transfer Control 0
+ */
+register SXFRCTL0 {
+ address 0x03C
+ access_mode RW
+ modes M_SCSI
+ field DFON 0x80
+ field DFPEXP 0x40
+ field BIOSCANCELEN 0x10
+ field SPIOEN 0x08
+}
+
+/*
+ * SCSI Transfer Control 1
+ */
+register SXFRCTL1 {
+ address 0x03D
+ access_mode RW
+ modes M_SCSI
+ field BITBUCKET 0x80
+ field ENSACHK 0x40
+ field ENSPCHK 0x20
+ field STIMESEL 0x18
+ field ENSTIMER 0x04
+ field ACTNEGEN 0x02
+ field STPWEN 0x01
+}
+
+/*
+ * SCSI Transfer Control 2
+ */
+register SXFRCTL2 {
+ address 0x03E
+ access_mode RW
+ modes M_SCSI
+ field AUTORSTDIS 0x10
+ field CMDDMAEN 0x08
+ field 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
+ field FIFO1FREE 0x20
+ field FIFO0FREE 0x10
+ /*
+ * On the B, this enum only works
+ * in the read direction. For writes,
+ * you must use the B version of the
+ * CURRFIFO_0 definition which is defined
+ * as a constant outside of this register
+ * definition to avoid confusing the
+ * register pretty printing code.
+ */
+ enum CURRFIFO 0x03 {
+ CURRFIFO_0,
+ CURRFIFO_1,
+ CURRFIFO_NONE 0x3
+ }
+}
+
+const B_CURRFIFO_0 0x2
+
+/*
+ * 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
+ field CDO 0x80
+ field IOO 0x40
+ field MSGO 0x20
+ field ATNO 0x10
+ field SELO 0x08
+ field BSYO 0x04
+ field REQO 0x02
+ field ACKO 0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+ enum PHASE_MASK CDO|IOO|MSGO {
+ P_DATAOUT 0x0,
+ P_DATAIN IOO,
+ P_DATAOUT_DT P_DATAOUT|MSGO,
+ P_DATAIN_DT P_DATAIN|MSGO,
+ P_COMMAND CDO,
+ P_MESGOUT CDO|MSGO,
+ P_STATUS CDO|IOO,
+ P_MESGIN CDO|IOO|MSGO
+ }
+}
+
+register SCSISIGI {
+ address 0x041
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CDI 0x80
+ field IOI 0x40
+ field MSGI 0x20
+ field ATNI 0x10
+ field SELI 0x08
+ field BSYI 0x04
+ field REQI 0x02
+ field ACKI 0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+ enum PHASE_MASK CDO|IOO|MSGO {
+ P_DATAOUT 0x0,
+ P_DATAIN IOO,
+ P_DATAOUT_DT P_DATAOUT|MSGO,
+ P_DATAIN_DT P_DATAIN|MSGO,
+ P_COMMAND CDO,
+ P_MESGOUT CDO|MSGO,
+ P_STATUS CDO|IOO,
+ P_MESGIN CDO|IOO|MSGO
+ }
+}
+
+/*
+ * 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
+ field STATUS_PHASE 0x20
+ field COMMAND_PHASE 0x10
+ field MSG_IN_PHASE 0x08
+ field MSG_OUT_PHASE 0x04
+ field DATA_PHASE_MASK 0x03 {
+ DATA_OUT_PHASE 0x01,
+ DATA_IN_PHASE 0x02
+ }
+}
+
+/*
+ * 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
+ field CLKOUT 0x80
+ field 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
+ field SELID_MASK 0xf0
+ field 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
+ field DIAGLEDEN 0x80
+ field DIAGLEDON 0x40
+ field ENAB40 0x08 /* LVD transceiver active */
+ field ENAB20 0x04 /* SE/HVD transceiver active */
+ field SELWIDE 0x02
+}
+
+/*
+ * Option Mode
+ */
+register OPTIONMODE {
+ address 0x04A
+ access_mode RW
+ modes M_CFG
+ field BIOSCANCTL 0x80
+ field AUTOACKEN 0x40
+ field BIASCANCTL 0x20
+ field BUSFREEREV 0x10
+ field ENDGFORMCHK 0x04
+ field 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
+ field TARGET 0x80 /* Board acting as target */
+ field SELDO 0x40 /* Selection Done */
+ field SELDI 0x20 /* Board has been selected */
+ field SELINGO 0x10 /* Selection In Progress */
+ field IOERR 0x08 /* LVD Tranceiver mode changed */
+ field OVERRUN 0x04 /* SCSI Offset overrun detected */
+ field SPIORDY 0x02 /* SCSI PIO Ready */
+ field 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
+ field CLRSELDO 0x40
+ field CLRSELDI 0x20
+ field CLRSELINGO 0x10
+ field CLRIOERR 0x08
+ field CLROVERRUN 0x04
+ field CLRSPIORDY 0x02
+ field 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
+ field ENSELDO 0x40
+ field ENSELDI 0x20
+ field ENSELINGO 0x10
+ field ENIOERR 0x08
+ field ENOVERRUN 0x04
+ field ENSPIORDY 0x02
+ field ENARBDO 0x01
+}
+
+/*
+ * SCSI Status 1
+ */
+register SSTAT1 {
+ address 0x04C
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field SELTO 0x80
+ field ATNTARG 0x40
+ field SCSIRSTI 0x20
+ field PHASEMIS 0x10
+ field BUSFREE 0x08
+ field SCSIPERR 0x04
+ field STRB2FAST 0x02
+ field 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
+ field CLRSELTIMEO 0x80
+ field CLRATNO 0x40
+ field CLRSCSIRSTI 0x20
+ field CLRBUSFREE 0x08
+ field CLRSCSIPERR 0x04
+ field CLRSTRB2FAST 0x02
+ field CLRREQINIT 0x01
+}
+
+/*
+ * SCSI Status 2
+ */
+register SSTAT2 {
+ address 0x04d
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field BUSFREETIME 0xc0 {
+ BUSFREE_LQO 0x40,
+ BUSFREE_DFF0 0x80,
+ BUSFREE_DFF1 0xC0
+ }
+ field NONPACKREQ 0x20
+ field EXP_ACTIVE 0x10 /* SCSI Expander Active */
+ field BSYX 0x08 /* Busy Expander */
+ field WIDE_RES 0x04 /* Modes 0 and 1 only */
+ field SDONE 0x02 /* Modes 0 and 1 only */
+ field 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
+ field CLRNONPACKREQ 0x20
+ field CLRWIDE_RES 0x04 /* Modes 0 and 1 only */
+ field CLRSDONE 0x02 /* Modes 0 and 1 only */
+ field CLRDMADONE 0x01 /* Modes 0 and 1 only */
+}
+
+/*
+ * SCSI Interrupt Mode 2
+ */
+register SIMODE2 {
+ address 0x04D
+ access_mode RW
+ modes M_CFG
+ field ENWIDE_RES 0x04
+ field ENSDONE 0x02
+ field ENDMADONE 0x01
+}
+
+/*
+ * Physical Error Diagnosis
+ */
+register PERRDIAG {
+ address 0x04E
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field HIZERO 0x80
+ field HIPERR 0x40
+ field PREVPHASE 0x20
+ field PARITYERR 0x10
+ field AIPERR 0x08
+ field CRCERR 0x04
+ field DGFORMERR 0x02
+ field 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
+ field LQIATNQAS 0x20
+ field LQICRCT1 0x10
+ field LQICRCT2 0x08
+ field LQIBADLQT 0x04
+ field LQIATNLQ 0x02
+ field LQIATNCMD 0x01
+}
+
+/*
+ * Clear LQI Interrupts 0
+ */
+register CLRLQIINT0 {
+ address 0x050
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRLQIATNQAS 0x20
+ field CLRLQICRCT1 0x10
+ field CLRLQICRCT2 0x08
+ field CLRLQIBADLQT 0x04
+ field CLRLQIATNLQ 0x02
+ field CLRLQIATNCMD 0x01
+}
+
+/*
+ * LQI Manager Interrupt Mode 0
+ */
+register LQIMODE0 {
+ address 0x050
+ access_mode RW
+ modes M_CFG
+ field ENLQIATNQASK 0x20
+ field ENLQICRCT1 0x10
+ field ENLQICRCT2 0x08
+ field ENLQIBADLQT 0x04
+ field ENLQIATNLQ 0x02
+ field ENLQIATNCMD 0x01
+}
+
+/*
+ * LQI Manager Status 1
+ */
+register LQISTAT1 {
+ address 0x051
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQIPHASE_LQ 0x80
+ field LQIPHASE_NLQ 0x40
+ field LQIABORT 0x20
+ field LQICRCI_LQ 0x10
+ field LQICRCI_NLQ 0x08
+ field LQIBADLQI 0x04
+ field LQIOVERI_LQ 0x02
+ field LQIOVERI_NLQ 0x01
+}
+
+/*
+ * Clear LQI Manager Interrupts1
+ */
+register CLRLQIINT1 {
+ address 0x051
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRLQIPHASE_LQ 0x80
+ field CLRLQIPHASE_NLQ 0x40
+ field CLRLIQABORT 0x20
+ field CLRLQICRCI_LQ 0x10
+ field CLRLQICRCI_NLQ 0x08
+ field CLRLQIBADLQI 0x04
+ field CLRLQIOVERI_LQ 0x02
+ field CLRLQIOVERI_NLQ 0x01
+}
+
+/*
+ * LQI Manager Interrupt Mode 1
+ */
+register LQIMODE1 {
+ address 0x051
+ access_mode RW
+ modes M_CFG
+ field ENLQIPHASE_LQ 0x80
+ field ENLQIPHASE_NLQ 0x40
+ field ENLIQABORT 0x20
+ field ENLQICRCI_LQ 0x10
+ field ENLQICRCI_NLQ 0x08
+ field ENLQIBADLQI 0x04
+ field ENLQIOVERI_LQ 0x02
+ field ENLQIOVERI_NLQ 0x01
+}
+
+/*
+ * LQI Manager Status 2
+ */
+register LQISTAT2 {
+ address 0x052
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field PACKETIZED 0x80
+ field LQIPHASE_OUTPKT 0x40
+ field LQIWORKONLQ 0x20
+ field LQIWAITFIFO 0x10
+ field LQISTOPPKT 0x08
+ field LQISTOPLQ 0x04
+ field LQISTOPCMD 0x02
+ field LQIGSAVAIL 0x01
+}
+
+/*
+ * SCSI Status 3
+ */
+register SSTAT3 {
+ address 0x053
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field NTRAMPERR 0x02
+ field OSRAMPERR 0x01
+}
+
+/*
+ * Clear SCSI Status 3
+ */
+register CLRSINT3 {
+ address 0x053
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRNTRAMPERR 0x02
+ field CLROSRAMPERR 0x01
+}
+
+/*
+ * SCSI Interrupt Mode 3
+ */
+register SIMODE3 {
+ address 0x053
+ access_mode RW
+ modes M_CFG
+ field ENNTRAMPERR 0x02
+ field ENOSRAMPERR 0x01
+}
+
+/*
+ * LQO Manager Status 0
+ */
+register LQOSTAT0 {
+ address 0x054
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQOTARGSCBPERR 0x10
+ field LQOSTOPT2 0x08
+ field LQOATNLQ 0x04
+ field LQOATNPKT 0x02
+ field LQOTCRC 0x01
+}
+
+/*
+ * Clear LQO Manager interrupt 0
+ */
+register CLRLQOINT0 {
+ address 0x054
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRLQOTARGSCBPERR 0x10
+ field CLRLQOSTOPT2 0x08
+ field CLRLQOATNLQ 0x04
+ field CLRLQOATNPKT 0x02
+ field CLRLQOTCRC 0x01
+}
+
+/*
+ * LQO Manager Interrupt Mode 0
+ */
+register LQOMODE0 {
+ address 0x054
+ access_mode RW
+ modes M_CFG
+ field ENLQOTARGSCBPERR 0x10
+ field ENLQOSTOPT2 0x08
+ field ENLQOATNLQ 0x04
+ field ENLQOATNPKT 0x02
+ field ENLQOTCRC 0x01
+}
+
+/*
+ * LQO Manager Status 1
+ */
+register LQOSTAT1 {
+ address 0x055
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQOINITSCBPERR 0x10
+ field LQOSTOPI2 0x08
+ field LQOBADQAS 0x04
+ field LQOBUSFREE 0x02
+ field LQOPHACHGINPKT 0x01
+}
+
+/*
+ * Clear LOQ Interrupt 1
+ */
+register CLRLQOINT1 {
+ address 0x055
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRLQOINITSCBPERR 0x10
+ field CLRLQOSTOPI2 0x08
+ field CLRLQOBADQAS 0x04
+ field CLRLQOBUSFREE 0x02
+ field CLRLQOPHACHGINPKT 0x01
+}
+
+/*
+ * LQO Manager Interrupt Mode 1
+ */
+register LQOMODE1 {
+ address 0x055
+ access_mode RW
+ modes M_CFG
+ field ENLQOINITSCBPERR 0x10
+ field ENLQOSTOPI2 0x08
+ field ENLQOBADQAS 0x04
+ field ENLQOBUSFREE 0x02
+ field ENLQOPHACHGINPKT 0x01
+}
+
+/*
+ * LQO Manager Status 2
+ */
+register LQOSTAT2 {
+ address 0x056
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQOPKT 0xE0
+ field LQOWAITFIFO 0x10
+ field LQOPHACHGOUTPKT 0x02 /* outside of packet boundaries. */
+ field 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
+ field ENSELTIMO 0x80
+ field ENATNTARG 0x40
+ field ENSCSIRST 0x20
+ field ENPHASEMIS 0x10
+ field ENBUSFREE 0x08
+ field ENSCSIPERR 0x04
+ field ENSTRB2FAST 0x02
+ field 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
+ field DFFBITBUCKET 0x08
+ field CLRSHCNT 0x04
+ field CLRCHN 0x02
+ field RSTCHN 0x01
+}
+
+/*
+ * Next SCSI Control Block
+ */
+register NEXTSCB {
+ address 0x05A
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/* Rev B only. */
+register LQOSCSCTL {
+ address 0x05A
+ access_mode RW
+ size 1
+ modes M_CFG
+ field LQOH2A_VERSION 0x80
+ field LQONOCHKOVER 0x01
+}
+
+/*
+ * SEQ Interrupts
+ */
+register SEQINTSRC {
+ address 0x05B
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field CTXTDONE 0x40
+ field SAVEPTRS 0x20
+ field CFG4DATA 0x10
+ field CFG4ISTAT 0x08
+ field CFG4TSTAT 0x04
+ field CFG4ICMD 0x02
+ field CFG4TCMD 0x01
+}
+
+/*
+ * Clear Arp Interrupts
+ */
+register CLRSEQINTSRC {
+ address 0x05B
+ access_mode WO
+ modes M_DFF0, M_DFF1
+ field CLRCTXTDONE 0x40
+ field CLRSAVEPTRS 0x20
+ field CLRCFG4DATA 0x10
+ field CLRCFG4ISTAT 0x08
+ field CLRCFG4TSTAT 0x04
+ field CLRCFG4ICMD 0x02
+ field CLRCFG4TCMD 0x01
+}
+
+/*
+ * SEQ Interrupt Enabled (Shared)
+ */
+register SEQIMODE {
+ address 0x05C
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field ENCTXTDONE 0x40
+ field ENSAVEPTRS 0x20
+ field ENCFG4DATA 0x10
+ field ENCFG4ISTAT 0x08
+ field ENCFG4TSTAT 0x04
+ field ENCFG4ICMD 0x02
+ field 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
+ field SHCNTNEGATIVE 0x40 /* Rev B or higher */
+ field SHCNTMINUS1 0x20 /* Rev B or higher */
+ field LASTSDONE 0x10
+ field SHVALID 0x08
+ field DLZERO 0x04 /* FIFO data ends on packet boundary. */
+ field DATAINFIFO 0x02
+ field FIFOFREE 0x01
+}
+
+/*
+ * CRC Control
+ */
+register CRCCONTROL {
+ address 0x05d
+ access_mode RW
+ modes M_CFG
+ field CRCVALCHKEN 0x40
+}
+
+/*
+ * SCSI Test Control
+ */
+register SCSITEST {
+ address 0x05E
+ access_mode RW
+ modes M_CFG
+ field CNTRTEST 0x08
+ field 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
+ field DISABLE_OE 0x80
+ field PDN_IDIST 0x04
+ field 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
+ field PPROPT_PACE 0x08
+ field PPROPT_QAS 0x04
+ field PPROPT_DT 0x02
+ field PPROPT_IUT 0x01
+}
+
+/*
+ * Data Transfer Negotiation Data - Connection Options
+ */
+register NEGCONOPTS {
+ address 0x064
+ access_mode RW
+ modes M_SCSI
+ field ENSNAPSHOT 0x40
+ field RTI_WRTDIS 0x20
+ field RTI_OVRDTRN 0x10
+ field ENSLOWCRC 0x08
+ field ENAUTOATNI 0x04
+ field ENAUTOATNO 0x02
+ field WIDEXFER 0x01
+}
+
+/*
+ * Negotiation Table Annex Column Index.
+ */
+register ANNEXCOL {
+ address 0x065
+ access_mode RW
+ modes M_SCSI
+}
+
+register SCSCHKN {
+ address 0x066
+ access_mode RW
+ modes M_CFG
+ field STSELSKIDDIS 0x40
+ field CURRFIFODEF 0x20
+ field WIDERESEN 0x10
+ field SDONEMSKDIS 0x08
+ field DFFACTCLR 0x04
+ field SHVALIDSTDIS 0x02
+ field LSTSGCLRDIS 0x01
+}
+
+const AHD_ANNEXCOL_PER_DEV0 4
+const AHD_NUM_PER_DEV_ANNEXCOLS 4
+const AHD_ANNEXCOL_PRECOMP_SLEW 4
+const AHD_PRECOMP_MASK 0x07
+const AHD_PRECOMP_SHIFT 0
+const AHD_PRECOMP_CUTBACK_17 0x04
+const AHD_PRECOMP_CUTBACK_29 0x06
+const AHD_PRECOMP_CUTBACK_37 0x07
+const AHD_SLEWRATE_MASK 0x78
+const AHD_SLEWRATE_SHIFT 3
+/*
+ * Rev A has only a single bit of slew adjustment.
+ * Rev B has 4 bits.
+ */
+const AHD_SLEWRATE_DEF_REVA 0x01
+const AHD_SLEWRATE_DEF_REVB 0x08
+
+/* Rev A does not have any amplitude setting. */
+const AHD_ANNEXCOL_AMPLITUDE 6
+const AHD_AMPLITUDE_MASK 0x7
+const AHD_AMPLITUDE_SHIFT 0
+const AHD_AMPLITUDE_DEF 0x7
+
+/*
+ * 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
+ field PLL_VCOSEL 0x80
+ field PLL_PWDN 0x40
+ field PLL_NS 0x30
+ field PLL_ENLUD 0x08
+ field PLL_ENLPF 0x04
+ field PLL_DLPF 0x02
+ field 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
+ field PLL_CNTEN 0x80
+ field PLL_CNTCLR 0x40
+ field 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
+ field PLL_VCOSEL 0x80
+ field PLL_PWDN 0x40
+ field PLL_NS 0x30
+ field PLL_ENLUD 0x08
+ field PLL_ENLPF 0x04
+ field PLL_DLPF 0x02
+ field 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
+ field PLL_CNTEN 0x80
+ field PLL_CNTCLR 0x40
+ field 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
+ field AUSCBPTR_EN 0x80
+ field SCBPTR_ADDR 0x38
+ field 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
+ field CCSGDONE 0x80
+ field SG_CACHE_AVAIL 0x10
+ field CCSGENACK 0x08
+ mask CCSGEN 0x0C
+ field SG_FETCH_REQ 0x02
+ field CCSGRESET 0x01
+}
+
+/*
+ * CMD SCB Control
+ */
+register CCSCBCTL {
+ address 0x0AD
+ access_mode RW
+ modes M_CCHAN
+ field CCSCBDONE 0x80
+ field ARRDONE 0x40
+ field CCARREN 0x10
+ field CCSCBEN 0x08
+ field CCSCBDIR 0x04
+ field CCSCBRESET 0x01
+}
+
+/*
+ * CMC Ram BIST
+ */
+register CMC_RAMBIST {
+ address 0x0AD
+ access_mode RW
+ modes M_CFG
+ field SG_ELEMENT_SIZE 0x80
+ field SCBRAMBIST_FAIL 0x40
+ field SG_BIST_FAIL 0x20
+ field SG_BIST_EN 0x10
+ field CMC_BUFFER_BIST_FAIL 0x02
+ field 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
+ field FLEXDMAERR 0x02
+ field 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
+ field FLXARBACK 0x80
+ field FLXARBREQ 0x40
+ field BRDADDR 0x38
+ field BRDEN 0x04
+ field BRDRW 0x02
+ field 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
+ field INIT_DONE 0x80
+ field SEEOPCODE 0x70
+ field LDALTID_L 0x08
+ field SEEARBACK 0x04
+ field SEEBUSY 0x02
+ field SEESTART 0x01
+}
+
+/*
+ * Serial EEPROM Control
+ */
+register SEECTL {
+ address 0x0BE
+ access_mode RW
+ modes M_SCSI
+ field SEEOPCODE 0x70 {
+ SEEOP_ERASE 0x70,
+ SEEOP_READ 0x60,
+ SEEOP_WRITE 0x50,
+ /*
+ * The following four commands use special
+ * addresses for differentiation.
+ */
+ SEEOP_ERAL 0x40
+ }
+ mask SEEOP_EWEN 0x40
+ mask SEEOP_WALL 0x40
+ mask SEEOP_EWDS 0x40
+ field SEERST 0x02
+ field 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
+ field FLTRDISABLE 0x20
+ field EDGESENSE 0x10
+ field DSPFCNTSEL 0x0F
+}
+
+/*
+ * DSP Data Channel Control
+ */
+register DSPDATACTL {
+ address 0x0C1
+ access_mode RW
+ modes M_CFG
+ field BYPASSENAB 0x80
+ field DESQDIS 0x10
+ field RCVROFFSTDIS 0x04
+ field 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
+ field MANREQCTL 0xC0
+ field MANREQDLY 0x3F
+}
+
+/*
+ * DSP ACK Control
+ */
+register DSPACKCTL {
+ address 0x0C3
+ access_mode RW
+ modes M_CFG
+ field MANACKCTL 0xC0
+ field 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
+ field AUTOINCEN 0x80
+ field DSPSEL 0x1F
+}
+
+const NUMDSPS 0x14
+
+/*
+ * Write Bias Control
+ */
+register WRTBIASCTL {
+ address 0x0C5
+ access_mode WO
+ modes M_CFG
+ field AUTOXBCDIS 0x80
+ field XMITMANVAL 0x3F
+}
+
+/*
+ * Currently the WRTBIASCTL is the same as the default.
+ */
+const WRTBIASCTL_HP_DEFAULT 0x0
+
+/*
+ * Receiver Bias Control
+ */
+register RCVRBIOSCTL {
+ address 0x0C6
+ access_mode WO
+ modes M_CFG
+ field AUTORBCDIS 0x80
+ field 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 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 Debug Control
+ */
+register DFDBCTL {
+ address 0x0CB
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field DFF_CIO_WR_RDY 0x20
+ field DFF_CIO_RD_RDY 0x10
+ field DFF_DIR_ERR 0x08
+ field DFF_RAMBIST_FAIL 0x04
+ field DFF_RAMBIST_DONE 0x02
+ field DFF_RAMBIST_EN 0x01
+}
+
+/*
+ * 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
+ field PERRORDIS 0x80
+ field PAUSEDIS 0x40
+ field FAILDIS 0x20
+ field FASTMODE 0x10
+ field BRKADRINTEN 0x08
+ field STEP 0x04
+ field SEQRESET 0x02
+ field LOADRAM 0x01
+}
+
+/*
+ * Sequencer Control 1
+ * Instruction RAM Diagnostics
+ */
+register SEQCTL1 {
+ address 0x0D7
+ access_mode RW
+ field OVRLAY_DATA_CHK 0x08
+ field RAMBIST_DONE 0x04
+ field RAMBIST_FAIL 0x02
+ field RAMBIST_EN 0x01
+}
+
+/*
+ * Sequencer Flags
+ * Zero and Carry state of the ALU.
+ */
+register FLAGS {
+ address 0x0D8
+ access_mode RO
+ field ZERO 0x02
+ field CARRY 0x01
+}
+
+/*
+ * Sequencer Interrupt Control
+ */
+register SEQINTCTL {
+ address 0x0D9
+ access_mode RW
+ field INTVEC1DSL 0x80
+ field INT1_CONTEXT 0x20
+ field SCS_SEQ_INT1M1 0x10
+ field SCS_SEQ_INT1M0 0x08
+ field INTMASK2 0x04
+ field INTMASK1 0x02
+ field 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
+ field 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
+ }
+ REG_ISR {
+ size 2
+ }
+ SG_STATE {
+ size 1
+ field SEGS_AVAIL 0x01
+ field LOADING_NEEDED 0x02
+ field 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 legacy 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
+ field PRELOADEN 0x80
+ field WIDEODD 0x40
+ field SCSIEN 0x20
+ field SDMAEN 0x10
+ field SDMAENACK 0x10
+ field HDMAEN 0x08
+ field HDMAENACK 0x08
+ field DIRECTION 0x04 /* Set indicates PCI->SCSI */
+ field FIFOFLUSH 0x02
+ field FIFORESET 0x01
+ }
+ SEQ_FLAGS {
+ size 1
+ field NOT_IDENTIFIED 0x80
+ field NO_CDB_SENT 0x40
+ field TARGET_CMD_IS_TAGGED 0x40
+ field DPHASE 0x20
+ /* Target flags */
+ field TARG_CMD_PENDING 0x10
+ field CMDPHASE_PENDING 0x08
+ field DPHASE_PENDING 0x04
+ field SPHASE_PENDING 0x02
+ field 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
+ field CDI 0x80
+ field IOI 0x40
+ field MSGI 0x20
+ field P_BUSFREE 0x01
+ enum PHASE_MASK CDO|IOO|MSGO {
+ P_DATAOUT 0x0,
+ P_DATAIN IOO,
+ P_DATAOUT_DT P_DATAOUT|MSGO,
+ P_DATAIN_DT P_DATAIN|MSGO,
+ P_COMMAND CDO,
+ P_MESGOUT CDO|MSGO,
+ P_STATUS CDO|IOO,
+ P_MESGIN CDO|IOO|MSGO
+ }
+ }
+ /*
+ * Value to "or" into the SCBPTR[1] value to
+ * indicate that an entry in the QINFIFO is valid.
+ */
+ QOUTFIFO_ENTRY_VALID_TAG {
+ size 1
+ }
+ /*
+ * 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
+ field MANUALCTL 0x40
+ field ENSELI 0x20
+ field ENRSELI 0x10
+ field MANUALP 0x0C
+ field ENAUTOATNP 0x02
+ field ALTSTIM 0x01
+ }
+
+ /*
+ * The initiator specified tag for this target mode transaction.
+ */
+ INITIATOR_TAG {
+ size 1
+ }
+
+ SEQ_FLAGS2 {
+ size 1
+ field TARGET_MSG_PENDING 0x02
+ field SELECTOUT_QFROZEN 0x04
+ }
+
+ ALLOCFIFO_SCBPTR {
+ size 2
+ }
+
+ /*
+ * The maximum amount of time to wait, when interrupt coalessing
+ * is enabled, before issueing a CMDCMPLT interrupt for a completed
+ * command.
+ */
+ INT_COALESSING_TIMER {
+ size 2
+ }
+
+ /*
+ * The maximum number of commands to coaless into a single interrupt.
+ * Actually the 2's complement of that value to simplify sequencer
+ * code.
+ */
+ INT_COALESSING_MAXCMDS {
+ size 1
+ }
+
+ /*
+ * The minimum number of commands still outstanding required
+ * to continue coalessing (2's compliment of value).
+ */
+ INT_COALESSING_MINCMDS {
+ size 1
+ }
+
+ /*
+ * Number of commands "in-flight".
+ */
+ CMDS_PENDING {
+ size 2
+ }
+
+ /*
+ * The count of commands that have been coalessed.
+ */
+ INT_COALESSING_CMDCOUNT {
+ size 1
+ }
+
+ /*
+ * Since the HS_MAIBOX is self clearing, copy its contents to
+ * this position in scratch ram every time it changes.
+ */
+ LOCAL_HS_MAILBOX {
+ size 1
+ }
+ /*
+ * 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
+ field SG_ADDR_MASK 0xf8 /* In the last byte */
+ field SG_OVERRUN_RESID 0x02 /* In the first byte */
+ field 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_TAG {
+ size 2
+ }
+ SCB_CDB_LEN {
+ size 1
+ field SCB_CDB_LEN_PTR 0x80 /* CDB in host memory */
+ }
+ SCB_TASK_MANAGEMENT {
+ size 1
+ }
+ 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
+ field SG_LAST_SEG 0x80 /* In the fourth byte */
+ field SG_HIGH_ADDR_BITS 0x7F /* In the fourth byte */
+ }
+ SCB_SGPTR {
+ size 4
+ field SG_STATUS_VALID 0x04 /* In the first byte */
+ field SG_FULL_RESID 0x02 /* In the first byte */
+ field SG_LIST_NULL 0x01 /* In the first byte */
+ }
+ SCB_CONTROL {
+ size 1
+ field TARGET_SCB 0x80
+ field DISCENB 0x40
+ field TAG_ENB 0x20
+ field MK_MESSAGE 0x10
+ field STATUS_RCVD 0x08
+ field DISCONNECTED 0x04
+ field SCB_TAG_TYPE 0x03
+ }
+ SCB_SCSIID {
+ size 1
+ field TID 0xF0
+ field OID 0x0F
+ }
+ SCB_LUN {
+ size 1
+ field LID 0xff
+ }
+ SCB_TASK_ATTRIBUTE {
+ size 1
+ }
+ SCB_BUSADDR {
+ size 4
+ }
+ SCB_SPARE {
+ size 8
+ alias SCB_PKT_LUN
+ }
+ SCB_DISCONNECTED_LISTS {
+ size 8
+ }
+}
+
+/*********************************** Constants ********************************/
+const MK_MESSAGE_BIT_OFFSET 4
+const TID_SHIFT 4
+const TARGET_CMD_CMPLT 0xfe
+const INVALID_ADDR 0x80
+#define SCB_LIST_NULL 0xff
+#define QOUTFIFO_ENTRY_VALID_TOGGLE 0x80
+
+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 0xfe
+const MAX_OFFSET_PACED_BUG 0x7f
+/*
+ * Some 160 devices incorrectly accept 0xfe as a
+ * sync offset, but will overrun this value. Limit
+ * to 0x7f for speed lower than U320 which will
+ * avoid the persistent sync offset overruns.
+ */
+const MAX_OFFSET_NON_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_FULL_LUN 56
+const SCB_TRANSFER_SIZE_1BYTE_LUN 48
+/* PKT_OVERRUN_BUFSIZE must be a multiple of 256 less than 64K */
+const PKT_OVERRUN_BUFSIZE 512
+
+/*
+ * Timer parameters.
+ */
+const AHD_TIMER_US_PER_TICK 25
+const AHD_TIMER_MAX_TICKS 0xFFFF
+const AHD_TIMER_MAX_US (AHD_TIMER_MAX_TICKS * AHD_TIMER_US_PER_TICK)
+
+/*
+ * 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
+const SCB_TRANSFER_SIZE download
+
+/*
+ * BIOS SCB offsets
+ */
+const NVRAM_SCB_OFFSET 0x2C
diff --git a/drivers/scsi/aic7xxx/aic79xx.seq b/drivers/scsi/aic7xxx/aic79xx.seq
new file mode 100644
index 000000000000..7eb2f364859f
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.seq
@@ -0,0 +1,1868 @@
+/*
+ * Adaptec U320 device driver firmware for Linux and FreeBSD.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#77 $"
+PATCH_ARG_LIST = "struct ahd_softc *ahd"
+PREFIX = "ahd_"
+
+#include "aic79xx.reg"
+#include "scsi_message.h"
+
+restart:
+if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ test SEQINTCODE, 0xFF jz idle_loop;
+ SET_SEQINTCODE(NO_SEQINT)
+}
+
+idle_loop:
+
+ if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ /*
+ * Convert ERROR status into a sequencer
+ * interrupt to handle the case of an
+ * interrupt collision on the hardware
+ * setting of HWERR.
+ */
+ test ERROR, 0xFF jz no_error_set;
+ SET_SEQINTCODE(SAW_HWERR)
+no_error_set:
+ }
+ 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 . + 2;
+ call unexpected_nonpkt_phase_find_ctxt;
+ call idle_loop_gsfifo_in_scsi_mode;
+ call idle_loop_service_fifos;
+ call idle_loop_cchan;
+ jmp idle_loop;
+
+BEGIN_CRITICAL;
+idle_loop_gsfifo:
+ SET_MODE(M_SCSI, M_SCSI)
+idle_loop_gsfifo_in_scsi_mode:
+ test LQISTAT2, LQIGSAVAIL jz return;
+ /*
+ * 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.
+ *
+ * Cases 1 and 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_gsfifo_in_scsi_mode;
+BEGIN_CRITICAL;
+good_status_check_fifos:
+ clc;
+ bmov ARG_1, SCBPTR, 2;
+ SET_MODE(M_DFF0, M_DFF0)
+ call check_fifo;
+ jc return;
+ SET_MODE(M_DFF1, M_DFF1)
+ call check_fifo;
+ jc return;
+ SET_MODE(M_SCSI, M_SCSI)
+ jmp 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 jz longjmp;
+ ret;
+
+idle_loop_cchan:
+ SET_MODE(M_CCHAN, M_CCHAN)
+ test QOFF_CTLSTA, HS_MAILBOX_ACT jz hs_mailbox_empty;
+ mov LOCAL_HS_MAILBOX, HS_MAILBOX;
+ or QOFF_CTLSTA, HS_MAILBOX_ACT;
+hs_mailbox_empty:
+BEGIN_CRITICAL;
+ test CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle;
+ test CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog;
+ test CCSCBCTL, CCSCBDONE jz return;
+END_CRITICAL;
+ /* 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);
+ call qoutfifo_updated;
+ 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;
+ xor QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID_TOGGLE ret;
+
+qoutfifo_updated:
+ /*
+ * If there are more commands waiting to be dma'ed
+ * to the host, always coaless. Otherwise honor the
+ * host's wishes.
+ */
+ cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne coaless_by_count;
+ cmp COMPLETE_SCB_HEAD[1], SCB_LIST_NULL jne coaless_by_count;
+ test LOCAL_HS_MAILBOX, ENINT_COALESS jz issue_cmdcmplt;
+
+ /*
+ * If we have relatively few commands outstanding, don't
+ * bother waiting for another command to complete.
+ */
+ test CMDS_PENDING[1], 0xFF jnz coaless_by_count;
+ /* Add -1 so that jnc means <= not just < */
+ add A, -1, INT_COALESSING_MINCMDS;
+ add NONE, A, CMDS_PENDING;
+ jnc issue_cmdcmplt;
+
+ /*
+ * If coalessing, only coaless up to the limit
+ * provided by the host driver.
+ */
+coaless_by_count:
+ mov A, INT_COALESSING_MAXCMDS;
+ add NONE, A, INT_COALESSING_CMDCOUNT;
+ jc issue_cmdcmplt;
+ /*
+ * If the timer is not currently active,
+ * fire it up.
+ */
+ test INTCTL, SWTMINTMASK jz return;
+ bmov SWTIMER, INT_COALESSING_TIMER, 2;
+ mvi CLRSEQINTSTAT, CLRSEQ_SWTMRTO;
+ or INTCTL, SWTMINTEN|SWTIMER_START;
+ and INTCTL, ~SWTMINTMASK ret;
+
+issue_cmdcmplt:
+ mvi INTSTAT, CMDCMPLT;
+ clr INT_COALESSING_CMDCOUNT;
+ or INTCTL, SWTMINTMASK ret;
+
+BEGIN_CRITICAL;
+fetch_new_scb_inprog:
+ test CCSCBCTL, ARRDONE jz return;
+fetch_new_scb_done:
+ and CCSCBCTL, ~(CCARREN|CCSCBEN);
+ bmov REG0, SCBPTR, 2;
+ clr A;
+ add CMDS_PENDING, 1;
+ adc CMDS_PENDING[1], A;
+ /* 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;
+ /* Increment our position in the QINFIFO. */
+ mov NONE, SNSCB_QOFF;
+ /*
+ * 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 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 ret;
+first_new_scb:
+ bmov WAITING_TID_HEAD, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2 ret;
+END_CRITICAL;
+
+scbdma_idle:
+ /*
+ * Give precedence to downloading new SCBs to execute
+ * unless select-outs are currently frozen.
+ */
+ 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.
+ */
+ mov A, QOUTFIFO_ENTRY_VALID_TAG;
+ bmov COMPLETE_SCB_DMAINPROG_HEAD, COMPLETE_SCB_HEAD, 2;
+ mvi CCSCBCTL, CCSCBRESET;
+ bmov SCBHADDR, QOUTFIFO_NEXT_ADDR, 4;
+ bmov SCBPTR, COMPLETE_SCB_HEAD, 2;
+fill_qoutfifo_loop:
+ mov CCSCBRAM, SCBPTR;
+ or CCSCBRAM, A, SCBPTR[1];
+ mov NONE, SDSCB_QOFF;
+ inc INT_COALESSING_CMDCOUNT;
+ add CMDS_PENDING, -1;
+ adc CMDS_PENDING[1], -1;
+ 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 jnz fill_qoutfifo_done;
+ bmov SCBPTR, SCB_NEXT_COMPLETE, 2;
+ jmp 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 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;
+
+/*************************** Chip Bug Work Arounds ****************************/
+/*
+ * 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.
+ */
+if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) {
+set_mode_work_around:
+ mvi SEQINTCTL, INTVEC1DSL;
+ mov MODE_PTR, SINDEX;
+ clr SEQINTCTL ret;
+
+toggle_dff_mode_work_around:
+ mvi SEQINTCTL, INTVEC1DSL;
+ xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+ clr SEQINTCTL ret;
+}
+
+
+if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+set_seqint_work_around:
+ mov SEQINTCODE, SINDEX;
+ mvi SEQINTCODE, NO_SEQINT ret;
+}
+
+/************************ Packetized LongJmp Routines *************************/
+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 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.
+ * In RevA hardware, both FIFOs must be free before we
+ * can allocate a FIFO for a non-packetized transaction.
+ */
+allocate_fifo_loop:
+ /*
+ * Do whatever work is required to free a FIFO.
+ */
+ call idle_loop_service_fifos;
+ SET_MODE(M_SCSI, M_SCSI)
+allocate_fifo:
+ if ((ahd->bugs & AHD_NONPACKFIFO_BUG) != 0) {
+ and A, FIFO0FREE|FIFO1FREE, DFFSTAT;
+ cmp A, FIFO0FREE|FIFO1FREE jne allocate_fifo_loop;
+ } else {
+ test DFFSTAT, FIFO1FREE jnz allocate_fifo1;
+ test DFFSTAT, FIFO0FREE jz allocate_fifo_loop;
+ mvi DFFSTAT, B_CURRFIFO_0;
+ SET_MODE(M_DFF0, M_DFF0)
+ bmov SCBPTR, ALLOCFIFO_SCBPTR, 2 ret;
+ }
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+allocate_fifo1:
+ mvi DFFSTAT, CURRFIFO_1;
+ SET_MODE(M_DFF1, M_DFF1)
+ bmov SCBPTR, ALLOCFIFO_SCBPTR, 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:
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+ /*
+ * This exposes a window whereby a
+ * busfree just after a selection will
+ * be missed, but there is not other safe
+ * way to enable busfree detection if
+ * the busfreerev function is broken.
+ */
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE;
+ }
+ 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;
+ 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;
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+ /*
+ * This exposes a window whereby a
+ * busfree just after a selection will
+ * be missed, but there is not other safe
+ * way to enable busfree detection if
+ * the busfreerev function is broken.
+ */
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE;
+ }
+ 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;
+
+ SET_SEQINTCODE(BAD_PHASE)
+ jmp ITloop; /* Try reading the bus again. */
+
+/*
+ * Command phase. Set up the DMA registers and let 'er rip.
+ */
+p_command:
+ test SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay;
+ SET_SEQINTCODE(PROTO_VIOLATION)
+p_command_okay:
+ 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.
+ */
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT;
+ SET_MODE(M_SCSI, M_SCSI)
+p_command_allocate_fifo:
+ bmov ALLOCFIFO_SCBPTR, SCBPTR, 2;
+ call allocate_fifo;
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ 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_xfer;
+p_command_embedded:
+ bmov SHCNT[0], SCB_CDB_LEN, 1;
+ bmov DFDAT, SCB_CDB_STORE, 16;
+ mvi DFCNTRL, SCSIEN;
+p_command_xfer:
+ and SEQ_FLAGS, ~NO_CDB_SENT;
+ test DFCNTRL, SCSIEN jnz .;
+ /*
+ * DMA Channel automatically disabled.
+ * Don't allow a data phase if the command
+ * was not fully transferred.
+ */
+ 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 jnz mesgin_proto_violation;
+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 SCBPTR 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. */
+ SET_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:
+ mov SAVED_MODE, MODE_PTR;
+ SET_MODE(M_SCSI, M_SCSI)
+ shr NEGOADDR, 4, SAVED_SCSIID;
+ mov A, NEGCONOPTS;
+ RESTORE_MODE(SAVED_MODE)
+ test A, 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;
+ jmp mesgin_done;
+
+mesgin_proto_violation:
+ SET_SEQINTCODE(PROTO_VIOLATION)
+ 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(scsiid, lun) \
+ and A, 0xC0, scsiid; \
+ or SCBPTR, A, lun; \
+ clr SCBPTR[1]; \
+ and SINDEX, 0x30, scsiid; \
+ shr SINDEX, 3; /* Multiply by 2 */ \
+ 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 a tagged transaction.
+ */
+ and SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+ INDEX_DISC_LIST(SAVED_SCSIID, SAVED_LUN);
+ bmov DINDEX, SINDEX, 2;
+ bmov REG0, SINDIR, 2;
+ cmp REG0[1], SCB_LIST_NULL je snoop_tag;
+ /* Untagged. Clear the busy table entry and setup the SCB. */
+ bmov DINDIR, ALLONES, 2;
+ bmov SCBPTR, REG0, 2;
+ jmp setup_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. After receiving the tag, look for the SCB at SCB locations tag and
+ * tag + 256.
+ */
+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:
+ clr SCBPTR[1];
+ mvi SCBPTR call inb_next; /* tag value */
+verify_scb:
+ test SCB_CONTROL,DISCONNECTED jz 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_disconnected;
+verify_other_scb:
+ xor SCBPTR[1], 1;
+ test SCBPTR[1], 0xFF jnz verify_scb;
+ jmp not_found;
+
+/*
+ * 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;
+setup_SCB_disconnected:
+ and SCB_CONTROL,~DISCONNECTED;
+ clr SEQ_FLAGS; /* make note of IDENTIFY */
+ test SCB_SGPTR, SG_LIST_NULL jnz . + 3;
+ bmov ALLOCFIFO_SCBPTR, SCBPTR, 2;
+ call allocate_fifo;
+ /* 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:
+ SET_SEQINTCODE(NO_MATCH)
+ jmp mesgin_done;
+
+not_found_ITloop:
+ SET_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 we are identified and have successfully sent the CDB,
+ * any status will do. Optimize this fast path.
+ */
+ test SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation;
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
+
+ /*
+ * 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 jnz mesgin_proto_violation;
+
+ /*
+ * If we recevied good status but never successfully sent the
+ * cdb, abort the command.
+ */
+ test SCB_SCSI_STATUS,0xff jnz complete_accepted;
+ test SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;
+complete_accepted:
+
+ /*
+ * See if we attempted to deliver a message but the target ingnored us.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz complete_nomsg;
+ SET_SEQINTCODE(MKMSG_FAILED)
+complete_nomsg:
+ call queue_scb_completion;
+ jmp await_busfree;
+
+freeze_queue:
+ /* Cancel any pending select-out. */
+ test SSTAT0, SELDO|SELINGO 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:
+ if ((ahd->bugs & AHD_ABORT_LQI_BUG) == 0) {
+ /*
+ * Set MK_MESSAGE to trigger an abort should this SCB
+ * be referenced by a target even though it is not currently
+ * active.
+ */
+ or SCB_CONTROL, MK_MESSAGE;
+ }
+ 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
+ jnz mesgin_proto_violation;
+ or SCB_CONTROL,DISCONNECTED;
+ test SCB_CONTROL, TAG_ENB jnz await_busfree;
+queue_disc_scb:
+ bmov REG0, SCBPTR, 2;
+ INDEX_DISC_LIST(SAVED_SCSIID, SAVED_LUN);
+ bmov DINDEX, SINDEX, 2;
+ bmov DINDIR, REG0, 2;
+ bmov SCBPTR, REG0, 2;
+ /* FALLTHROUGH */
+await_busfree:
+ and SIMODE1, ~ENBUSFREE;
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) == 0) {
+ /*
+ * In the BUSFREEREV_BUG case, the
+ * busfree status was cleared at the
+ * beginning of the connection.
+ */
+ mvi CLRSINT1,CLRBUSFREE;
+ }
+ mov NONE, SCSIDAT; /* Ack the last byte */
+ 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:
+ call clear_target_state;
+ test SSTAT1,REQINIT|BUSFREE jz .;
+ test SSTAT1, BUSFREE jnz idle_loop;
+ SET_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.
+ */
+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 or command 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;
+ SET_MODE(M_SCSI, M_SCSI)
+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 .;
+ test SSTAT1, SCSIPERR jnz phase_lock;
+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 .;
+ test SSTAT1, SCSIPERR jnz inb_next_wait;
+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 disable_ccsgen_fetch_done;
+ clr CCSGCTL;
+disable_ccsgen_fetch_done:
+ clr SG_STATE ret;
+
+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? */
+ test CCSGCTL, CCSGDONE jnz idle_sgfetch_complete;
+
+ /* Are we actively fetching segments? */
+ test CCSGCTL, CCSGENACK 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.
+ */
+ bmov SGHADDR, SCB_RESIDUAL_SGPTR, 4;
+ mvi SGHCNT, SG_PREFETCH_CNT;
+ if ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0) {
+ /*
+ * Need two instruction between "touches" of SGHADDR.
+ */
+ nop;
+ }
+ and SGHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+ 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;
+ clr CCSGCTL;
+ 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.
+ */
+ if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0) {
+ /*
+ * Use SCSIENWRDIS so that SCSIEN is never
+ * modified by this operation.
+ */
+ or DFCNTRL, PRELOADEN|SCSIENWRDIS|HDMAEN;
+ } else {
+ 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 REG_ISR, ~SG_FULL_RESID, SCB_SGPTR[0];
+ test SCB_DATACNT[3], SG_LAST_SEG jz . + 2;
+ or REG_ISR, LAST_SEG;
+ test DATA_COUNT_ODD, 0x1 jz . + 2;
+ or REG_ISR, ODD_SEG;
+ mov SG_CACHE_PRE, REG_ISR;
+ 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_handle_xfer:
+ call setjmp_setscb;
+ test SG_STATE, LOADING_NEEDED jnz service_fifo;
+p_data_clear_handler:
+ or LONGJMP_ADDR[1], INVALID_ADDR ret;
+
+p_data:
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed;
+ SET_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;
+ SET_SEQINTCODE(PDATA_REINIT)
+ jmp data_phase_inbounds;
+
+p_data_bitbucket:
+ /*
+ * Turn on `Bit Bucket' mode, wait until the target takes
+ * us to another phase, and then notify the host.
+ */
+ mov SAVED_MODE, MODE_PTR;
+ 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;
+ /* Wait for non-data phase. */
+ test SCSIPHASE, ~DATA_PHASE_MASK jz .;
+ and SXFRCTL1, ~BITBUCKET;
+ RESTORE_MODE(SAVED_MODE)
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ SET_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;
+ mov SAVED_MODE, MODE_PTR;
+ test SG_STATE, LOADING_NEEDED jz data_group_dma_loop;
+ call p_data_handle_xfer;
+data_group_dma_loop:
+ /*
+ * The transfer is complete if either the last segment
+ * completes or the target changes phase. Both conditions
+ * will clear SCSIEN.
+ */
+ call idle_loop_service_fifos;
+ call idle_loop_cchan;
+ call idle_loop_gsfifo;
+ RESTORE_MODE(SAVED_MODE)
+ 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;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+
+ 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 idle_loop_service_fifos;
+ RESTORE_MODE(SAVED_MODE)
+ 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 timer_isr:
+ call issue_cmdcmplt;
+ mvi CLRSEQINTSTAT, CLRSEQ_SWTMRTO;
+ if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) {
+ /*
+ * In H2A4, the mode pointer is not saved
+ * for intvec2, but is restored on iret.
+ * This can lead to the restoration of a
+ * bogus mode ptr. Manually clear the
+ * intmask bits and do a normal return
+ * to compensate.
+ */
+ and SEQINTCTL, ~(INTMASK2|INTMASK1) ret;
+ } else {
+ or SEQINTCTL, IRET ret;
+ }
+
+export seq_isr:
+ nop; /* Jumps in the first ISR instruction fail on Rev A. */
+ test SEQINTSRC, CFG4DATA jnz cfg4data_intr;
+ test SEQINTSRC, CFG4ISTAT jnz cfg4istat_intr;
+ test SEQINTSRC, SAVEPTRS jnz saveptr_intr;
+ test SEQINTSRC, CFG4ICMD jnz cfg4icmd_intr;
+ SET_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.
+ */
+ SET_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 are 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 jz return;
+ /* FALLTHROUGH */
+
+/*
+ * 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 this status and executing the common code for
+ * these two cases.
+ */
+pkt_saveptrs:
+BEGIN_CRITICAL;
+ if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ mov REG0, SEQINTSRC;
+ call calc_residual;
+ call save_pointers;
+ mvi CLRSEQINTSRC, CLRSAVEPTRS;
+ call disable_ccsgen;
+ or SEQIMODE, ENSAVEPTRS;
+ test DFCNTRL, DIRECTION jnz pkt_saveptrs_check_status;
+ test DFSTATUS, FIFOEMP jnz pkt_saveptrs_check_status;
+ /*
+ * Keep a handler around for this FIFO until it drains
+ * to the host to guarantee that we don't complete the
+ * command to the host before the data arrives.
+ */
+pkt_saveptrs_wait_fifoemp:
+ call setjmp;
+ test DFSTATUS, FIFOEMP jz return;
+pkt_saveptrs_check_status:
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ 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;
+
+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;
+ /*
+ * It is safe to skip the other FIFO check since
+ * we defer CLRCHN on SAVEPTRS until all data in
+ * the FIFO are seen by the host and a CFG4DATA
+ * in this FIFO for the same context is held off
+ * by hardware.
+ */
+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;
+ 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;
+
+check_status_overrun:
+ test SHCNT[2], 0xFF jz status_IU_done;
+ SET_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;
+ SET_SEQINTCODE(ENTERING_NONPACK)
+ jmp ITloop;
+
+illegal_phase:
+ SET_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:
+ SET_SEQINTCODE(CFG4OVERRUN)
+ call freeze_queue;
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0) {
+ or DFFSXFRCTL, DFFBITBUCKET;
+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, PRELOAD_AVAIL jz overrun_load_done;
+ call load_overrun_buf;
+ or DFCNTRL, PRELOADEN;
+overrun_load_done:
+ test SEQINTSRC, CTXTDONE jnz pkt_overrun_end;
+ } else {
+ test DFFSXFRCTL, DFFBITBUCKET jz 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] ret;
+}
+
+cfg4icmd_intr:
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
new file mode 100644
index 000000000000..5c0951af77c6
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -0,0 +1,9383 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2002 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#147 $
+ *
+ * $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
+
+/******************************** Globals *************************************/
+struct ahd_softc_tailq ahd_tailq = TAILQ_HEAD_INITIALIZER(ahd_tailq);
+
+/***************************** Lookup Tables **********************************/
+char *ahd_chip_names[] =
+{
+ "NONE",
+ "aic7901",
+ "aic7902",
+ "aic7901A"
+};
+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_handle_proto_violation(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 u_int ahd_sglist_size(struct ahd_softc *ahd);
+static u_int 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_add_col_list(struct ahd_softc *ahd,
+ struct scb *scb, u_int col_idx);
+static void ahd_rem_col_list(struct ahd_softc *ahd,
+ struct scb *scb);
+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;
+static ahd_callback_t ahd_stat_timer;
+#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);
+static int ahd_probe_stack_size(struct ahd_softc *ahd);
+#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;
+ 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;
+
+#ifdef 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 ********************************/
+/*
+ * Flush and completed commands that are sitting in the command
+ * complete queues down on the chip but have yet to be dma'ed back up.
+ */
+void
+ahd_flush_qoutfifo(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ ahd_mode_state saved_modes;
+ u_int saved_scbptr;
+ u_int ccscbctl;
+ u_int scbid;
+ u_int next_scbid;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+ saved_scbptr = ahd_get_scbptr(ahd);
+
+ /*
+ * Wait for any inprogress DMA to complete and clear DMA state
+ * if this if for an SCB in the qinfifo.
+ */
+ while ((ccscbctl = ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN)) != 0) {
+
+ if ((ccscbctl & (CCSCBDIR|CCARREN)) == (CCSCBDIR|CCARREN)) {
+ if ((ccscbctl & ARRDONE) != 0)
+ break;
+ } else if ((ccscbctl & CCSCBDONE) != 0)
+ break;
+ ahd_delay(200);
+ }
+ if ((ccscbctl & CCSCBDIR) != 0)
+ ahd_outb(ahd, CCSCBCTL, ccscbctl & ~(CCARREN|CCSCBEN));
+
+ /*
+ * Complete any SCBs that just finished being
+ * DMA'ed into the qoutfifo.
+ */
+ ahd_run_qoutfifo(ahd);
+
+ /*
+ * Manually update/complete any completed SCBs that are waiting to be
+ * DMA'ed back up to the host.
+ */
+ scbid = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
+ while (!SCBID_IS_NULL(scbid)) {
+ uint8_t *hscb_ptr;
+ u_int i;
+
+ ahd_set_scbptr(ahd, scbid);
+ next_scbid = ahd_inw(ahd, SCB_NEXT_COMPLETE);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: Warning - DMA-up and complete "
+ "SCB %d invalid\n", ahd_name(ahd), scbid);
+ continue;
+ }
+ hscb_ptr = (uint8_t *)scb->hscb;
+ for (i = 0; i < sizeof(struct hardware_scb); i++)
+ *hscb_ptr++ = ahd_inb(ahd, SCB_BASE + i);
+
+ ahd_complete_scb(ahd, scb);
+ scbid = next_scbid;
+ }
+ ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);
+
+ scbid = ahd_inw(ahd, COMPLETE_SCB_HEAD);
+ while (!SCBID_IS_NULL(scbid)) {
+
+ ahd_set_scbptr(ahd, scbid);
+ next_scbid = ahd_inw(ahd, SCB_NEXT_COMPLETE);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: Warning - Complete SCB %d invalid\n",
+ ahd_name(ahd), scbid);
+ continue;
+ }
+
+ ahd_complete_scb(ahd, scb);
+ scbid = next_scbid;
+ }
+ ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);
+
+ /*
+ * Restore state.
+ */
+ ahd_set_scbptr(ahd, saved_scbptr);
+ ahd_restore_modes(ahd, saved_modes);
+ ahd->flags |= AHD_UPDATE_PEND_CMDS;
+}
+
+void
+ahd_run_qoutfifo(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ u_int scb_index;
+
+ if ((ahd->flags & AHD_RUNNING_QOUTFIFO) != 0)
+ panic("ahd_run_qoutfifo recursion");
+ ahd->flags |= AHD_RUNNING_QOUTFIFO;
+ ahd_sync_qoutfifo(ahd, BUS_DMASYNC_POSTREAD);
+ while ((ahd->qoutfifo[ahd->qoutfifonext]
+ & QOUTFIFO_ENTRY_VALID_LE) == ahd->qoutfifonext_valid_tag) {
+
+ scb_index = ahd_le16toh(ahd->qoutfifo[ahd->qoutfifonext]
+ & ~QOUTFIFO_ENTRY_VALID_LE);
+ 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);
+ } else
+ ahd_complete_scb(ahd, scb);
+
+ ahd->qoutfifonext = (ahd->qoutfifonext+1) & (AHD_QOUT_SIZE-1);
+ if (ahd->qoutfifonext == 0)
+ ahd->qoutfifonext_valid_tag ^= QOUTFIFO_ENTRY_VALID_LE;
+ }
+ ahd->flags &= ~AHD_RUNNING_QOUTFIFO;
+}
+
+/************************* 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);
+ if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ /*
+ * Unpause the sequencer and let it clear
+ * SEQINT by writing NO_SEQINT to it. This
+ * will cause the sequencer to be paused again,
+ * which is the expected state of this routine.
+ */
+ ahd_unpause(ahd);
+ while (!ahd_is_paused(ahd))
+ ;
+ ahd_outb(ahd, CLRINT, CLRSEQINT);
+ }
+ ahd_update_modes(ahd);
+#ifdef 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.
+ */
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+ printf("%s: Assuming LQIPHASE_NLQ with "
+ "P0 assertion\n", ahd_name(ahd));
+#endif
+ }
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+ printf("%s: Entering NONPACK\n", ahd_name(ahd));
+#endif
+ break;
+ }
+ case INVALID_SEQINT:
+ printf("%s: Invalid Sequencer interrupt occurred.\n",
+ ahd_name(ahd));
+ ahd_dump_card_state(ahd);
+ 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;
+
+ 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);
+ }
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Unexpected command phase from "
+ "packetized target\n");
+ }
+#endif
+ break;
+ }
+ }
+ break;
+ }
+ case CFG4OVERRUN:
+ {
+ struct scb *scb;
+ u_int scb_index;
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ printf("%s: CFG4OVERRUN mode = %x\n", ahd_name(ahd),
+ ahd_inb(ahd, MODE_PTR));
+ }
+#endif
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ if (scb == NULL) {
+ /*
+ * Attempt to transfer to an SCB that is
+ * not outstanding.
+ */
+ ahd_assert_atn(ahd);
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd->msgout_buf[0] = MSG_ABORT_TASK;
+ ahd->msgout_len = 1;
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ /*
+ * Clear status received flag to prevent any
+ * attempt to complete this bogus SCB.
+ */
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb(ahd, SCB_CONTROL) & ~STATUS_RCVD);
+ }
+ break;
+ }
+ case DUMP_CARD_STATE:
+ {
+ ahd_dump_card_state(ahd);
+ break;
+ }
+ case PDATA_REINIT:
+ {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ 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));
+ }
+#endif
+ 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) >> 4);
+ printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+ "REG0 == 0x%x ACCUM = 0x%x\n",
+ ahd_inb(ahd, SAVED_SCSIID), ahd_inb(ahd, SAVED_LUN),
+ ahd_inw(ahd, REG0), 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_inw(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:
+ {
+ ahd_handle_proto_violation(ahd);
+ break;
+ }
+ 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;
+#ifdef AHD_DEBUG
+ u_int lastphase;
+#endif
+
+ scbindex = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbindex);
+#ifdef AHD_DEBUG
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ 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);
+ }
+#endif
+
+ /*
+ * 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;
+ }
+ case TRACEPOINT0:
+ case TRACEPOINT1:
+ case TRACEPOINT2:
+ case TRACEPOINT3:
+ printf("%s: Tracepoint %d\n", ahd_name(ahd),
+ seqintcode - TRACEPOINT0);
+ break;
+ case NO_SEQINT:
+ break;
+ case SAW_HWERR:
+ ahd_handle_hwerrint(ahd);
+ break;
+ default:
+ printf("%s: Unexpected SEQINTCODE %d\n", ahd_name(ahd),
+ seqintcode);
+ 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;
+ u_int busfreetime;
+
+ 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);
+ busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
+ 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 & (IOERR|OVERRUN|SELDI|SELDO);
+ 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) {
+ u_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);
+ /*
+ * A change in I/O mode is equivalent to a bus reset.
+ */
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/FALSE);
+ 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, 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);
+ 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);
+ } else {
+ struct ahd_devinfo devinfo;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_SELTO) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Saw Selection Timeout for SCB 0x%x\n",
+ scbid);
+ }
+#endif
+ /*
+ * 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.
+ */
+ ahd_scb_devinfo(ahd, &devinfo, scb);
+ ahd_force_renegotiation(ahd, &devinfo);
+ ahd_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+ ahd_freeze_devq(ahd, scb);
+ }
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_iocell_first_selection(ahd);
+ ahd_unpause(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 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;
+ 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;
+ }
+
+#ifdef 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 {
+ packetized = 0;
+ restart = ahd_handle_nonpkt_busfree(ahd);
+ }
+ /*
+ * Clear the busfree interrupt status. The setting of
+ * the interrupt is a pulse, so in a perfect world, we
+ * would not need to muck with the ENBUSFREE logic. This
+ * would ensure that if the bus moves on to another
+ * connection, busfree protection is still in force. If
+ * BUSFREEREV is broken, however, we must manually clear
+ * the ENBUSFREE if the busfree occurred during a non-pack
+ * connection so that we don't get false positives during
+ * future, packetized, connections.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRBUSFREE);
+ if (packetized == 0
+ && (ahd->bugs & AHD_BUSFREEREV_BUG) != 0)
+ ahd_outb(ahd, SIMODE1,
+ ahd_inb(ahd, SIMODE1) & ~ENBUSFREE);
+
+ 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);
+ ahd_dump_card_state(ahd);
+ ahd_clear_intstat(ahd);
+ ahd_unpause(ahd);
+ }
+}
+
+static void
+ahd_handle_transmission_error(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ u_int scbid;
+ u_int lqistat1;
+ u_int lqistat2;
+ u_int msg_out;
+ u_int curphase;
+ u_int lastphase;
+ u_int perrdiag;
+ u_int cur_col;
+ int silent;
+
+ scb = NULL;
+ 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)) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ printf("%s: NLQCRC found via LQISTATE\n",
+ ahd_name(ahd));
+ }
+#endif
+ 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);
+
+ /*
+ * Try to find the SCB associated with this error.
+ */
+ silent = FALSE;
+ if (lqistat1 == 0
+ || (lqistat1 & LQICRCI_NLQ) != 0) {
+ if ((lqistat1 & (LQICRCI_NLQ|LQIOVERI_NLQ)) != 0)
+ ahd_set_active_fifo(ahd);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL && SCB_IS_SILENT(scb))
+ silent = TRUE;
+ }
+
+ cur_col = 0;
+ if (silent == FALSE) {
+ printf("%s: Transmission error detected\n", ahd_name(ahd));
+ ahd_lqistat1_print(lqistat1, &cur_col, 50);
+ ahd_lastphase_print(lastphase, &cur_col, 50);
+ ahd_scsisigi_print(curphase, &cur_col, 50);
+ ahd_perrdiag_print(perrdiag, &cur_col, 50);
+ printf("\n");
+ ahd_dump_card_state(ahd);
+ }
+
+ if ((lqistat1 & (LQIOVERI_LQ|LQIOVERI_NLQ)) != 0) {
+ if (silent == FALSE) {
+ 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) {
+ /*
+ * 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
+ * LQIRETRY. 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.
+ */
+ if (silent == FALSE)
+ printf("LQICRC_NLQ\n");
+ 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;
+ }
+ } else if ((lqistat1 & LQIBADLQI) != 0) {
+ printf("Need to handle BADLQI!\n");
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ return;
+ } else if ((perrdiag & (PARITYERR|PREVPHASE)) == PARITYERR) {
+ if ((curphase & ~P_DATAIN_DT) != 0) {
+ /* Ack the byte. So we can continue. */
+ if (silent == FALSE)
+ 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;
+ if (scb != NULL && msg_out == MSG_INITIATOR_DET_ERR)
+ scb->flags |= SCB_TRANSMISSION_ERROR;
+ 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("LQIRETRY 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 based on busfreetime.
+ */
+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 saved_scbptr;
+ 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);
+ /*
+ * 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_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ scbid = ahd_inw(ahd, CURRSCB);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL)
+ panic("SCB not valid during LQOBUSFREE");
+ /*
+ * 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);
+ ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+ ahd_outb(ahd, CLRSINT0, CLRSELDO);
+
+ /*
+ * Update the waiting for selection queue so
+ * we restart on the correct SCB.
+ */
+ waiting_h = ahd_inw(ahd, WAITING_TID_HEAD);
+ saved_scbptr = ahd_get_scbptr(ahd);
+ 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);
+ }
+ ahd_set_scbptr(ahd, saved_scbptr);
+ if (scb->crc_retry_count < AHD_MAX_LQ_CRC_ERRORS) {
+ if (SCB_IS_SILENT(scb) == FALSE) {
+ ahd_print_path(ahd, scb);
+ printf("Probable outgoing LQ CRC error. "
+ "Retrying command\n");
+ }
+ scb->crc_retry_count++;
+ } else {
+ ahd_set_transaction_status(scb, CAM_UNCOR_PARITY);
+ ahd_freeze_scb(scb);
+ ahd_freeze_devq(ahd, scb);
+ }
+ /* Return unpausing the sequencer. */
+ return (0);
+ } else if ((ahd_inb(ahd, PERRDIAG) & PARITYERR) != 0) {
+ /*
+ * Ignore what are really parity errors that
+ * occur on the last REQ of a free running
+ * clock prior to going busfree. Some drives
+ * do not properly active negate just before
+ * going busfree resulting in a parity glitch.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRSCSIPERR|CLRBUSFREE);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MASKED_ERRORS) != 0)
+ printf("%s: Parity on last REQ detected "
+ "during busfree phase.\n",
+ ahd_name(ahd));
+#endif
+ /* 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;
+ u_int ppr_busfree;
+ 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;
+
+ ppr_busfree = (ahd->msg_flags & MSG_FLAG_EXPECT_PPR_BUSFREE) != 0;
+ 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;
+
+ if (scb == NULL) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Abort for unidentified "
+ "connection completed.\n");
+ /* restart the sequencer. */
+ return (1);
+ }
+ 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)
+ && ppr_busfree == 0) {
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ /*
+ * PPR Rejected. Try non-ppr negotiation
+ * and retry command.
+ */
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("PPR negotiation rejected busfree.\n");
+#endif
+ 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))
+ && ppr_busfree == 0) {
+ /*
+ * Negotiation Rejected. Go-async and
+ * retry command.
+ */
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Negotiation rejected busfree.\n");
+#endif
+ 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)) {
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Expected IDE Busfree\n");
+#endif
+ printerror = 0;
+ } else if ((ahd->msg_flags & MSG_FLAG_EXPECT_QASREJ_BUSFREE)
+ && ahd_sent_msg(ahd, AHDMSG_1B,
+ MSG_MESSAGE_REJECT, TRUE)) {
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Expected QAS Reject Busfree\n");
+#endif
+ printerror = 0;
+ }
+ }
+
+ /*
+ * The busfree required flag is honored at the end of
+ * the message phases. We check it last in case we
+ * had to send some other message that caused a busfree.
+ */
+ if (printerror != 0
+ && (lastphase == P_MESGIN || lastphase == P_MESGOUT)
+ && ((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_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 {
+#ifdef 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);
+}
+
+static void
+ahd_handle_proto_violation(struct ahd_softc *ahd)
+{
+ struct ahd_devinfo devinfo;
+ struct scb *scb;
+ u_int scbid;
+ u_int seq_flags;
+ u_int curphase;
+ u_int lastphase;
+ int found;
+
+ ahd_fetch_devinfo(ahd, &devinfo);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ seq_flags = ahd_inb(ahd, SEQ_FLAGS);
+ curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ 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.
+ */
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Target did not send an IDENTIFY message. "
+ "LASTPHASE = 0x%x.\n", lastphase);
+ scb = NULL;
+ } else if (scb == NULL) {
+ /*
+ * We don't seem to have an SCB active for this
+ * transaction. Print an error and reset the bus.
+ */
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("No SCB found during protocol violation\n");
+ goto proto_violation_reset;
+ } else {
+ ahd_set_transaction_status(scb, CAM_SEQUENCE_FAIL);
+ 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 ((lastphase & ~P_DATAIN_DT) == 0
+ || lastphase == P_COMMAND) {
+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_assert_atn(ahd);
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ if (scb == NULL) {
+ ahd_print_devinfo(ahd, &devinfo);
+ ahd->msgout_buf[0] = MSG_ABORT_TASK;
+ ahd->msgout_len = 1;
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ } else {
+ ahd_print_path(ahd, scb);
+ scb->flags |= SCB_ABORT;
+ }
+ printf("Protocol violation %s. Attempting to abort.\n",
+ ahd_lookup_phase_entry(curphase)->phasemsg);
+ }
+}
+
+/*
+ * 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;
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Forcing renegotiation\n");
+ }
+#endif
+ targ_info = ahd_fetch_transinfo(ahd,
+ devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target,
+ &tstate);
+ ahd_update_neg_request(ahd, devinfo, tstate,
+ targ_info, AHD_NEG_IF_NON_ASYNC);
+}
+
+#define AHD_MAX_STEPS 2000
+void
+ahd_clear_critical_section(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ int stepping;
+ int steps;
+ int first_instr;
+ 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;
+ first_instr = 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"
+ "%s: First Instruction 0x%x now 0x%x\n",
+ ahd_name(ahd), ahd_name(ahd), first_instr,
+ seqaddr);
+ ahd_dump_card_state(ahd);
+ panic("critical section loop");
+ }
+
+ steps++;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: Single stepping at 0x%x\n", ahd_name(ahd),
+ seqaddr);
+#endif
+ if (stepping == FALSE) {
+
+ first_instr = seqaddr;
+ 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, ENBUSFREE);
+ ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) | STEP);
+ stepping = TRUE;
+ }
+ ahd_outb(ahd, CLRSINT1, CLRBUSFREE);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ 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, SEQCTL0, ahd_inb(ahd, SEQCTL0) & ~STEP);
+ ahd_outb(ahd, SIMODE1, simode1);
+ }
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahd_clear_intstat(struct ahd_softc *ahd)
+{
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ /* Clear any interrupt conditions this may have caused */
+ ahd_outb(ahd, CLRLQIINT0, CLRLQIATNQAS|CLRLQICRCT1|CLRLQICRCT2
+ |CLRLQIBADLQT|CLRLQIATNLQ|CLRLQIATNCMD);
+ ahd_outb(ahd, CLRLQIINT1, CLRLQIPHASE_LQ|CLRLQIPHASE_NLQ|CLRLIQABORT
+ |CLRLQICRCI_LQ|CLRLQICRCI_NLQ|CLRLQIBADLQI
+ |CLRLQIOVERI_LQ|CLRLQIOVERI_NLQ|CLRNONPACKREQ);
+ ahd_outb(ahd, CLRLQOINT0, CLRLQOTARGSCBPERR|CLRLQOSTOPT2|CLRLQOATNLQ
+ |CLRLQOATNPKT|CLRLQOTCRC);
+ ahd_outb(ahd, CLRLQOINT1, CLRLQOINITSCBPERR|CLRLQOSTOPI2|CLRLQOBADQAS
+ |CLRLQOBUSFREE|CLRLQOPHACHGINPKT);
+ if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+ ahd_outb(ahd, CLRLQOINT0, 0);
+ ahd_outb(ahd, CLRLQOINT1, 0);
+ }
+ ahd_outb(ahd, CLRSINT3, CLRNTRAMPERR|CLROSRAMPERR);
+ ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+ |CLRBUSFREE|CLRSCSIPERR|CLRREQINIT);
+ ahd_outb(ahd, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO
+ |CLRIOERR|CLROVERRUN);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHD_DEBUG
+uint32_t ahd_debug = AHD_DEBUG_OPTS;
+#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;
+ uint32_t len;
+
+ addr = ahd_le64toh(sg_list[i].addr);
+ len = ahd_le32toh(sg_list[i].len);
+ printf("sg[%d] - Addr 0x%x%x : Length %d%s\n",
+ i,
+ (uint32_t)((addr >> 32) & 0xFFFFFFFF),
+ (uint32_t)(addr & 0xFFFFFFFF),
+ sg_list[i].len & AHD_SG_LEN_MASK,
+ (sg_list[i].len & AHD_DMA_LAST_SEG)
+ ? " Last" : "");
+ }
+ } else {
+ struct ahd_dma_seg *sg_list;
+
+ sg_list = (struct ahd_dma_seg*)scb->sg_list;
+ for (i = 0; i < scb->sg_count; i++) {
+ uint32_t len;
+
+ len = ahd_le32toh(sg_list[i].len);
+ printf("sg[%d] - Addr 0x%x%x : Length %d%s\n",
+ i,
+ (len >> 24) & SG_HIGH_ADDR_BITS,
+ ahd_le32toh(sg_list[i].addr),
+ len & AHD_SG_LEN_MASK,
+ len & AHD_DMA_LAST_SEG ? " Last" : "");
+ }
+ }
+ }
+}
+
+/************************* 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->width == MSG_EXT_WDTR_BUS_8_BIT) {
+ maxsync = MAX(maxsync, AHD_SYNCRATE_ULTRA2);
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ 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)
+{
+ 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 (*period > AHD_SYNCRATE_PACED)
+ *ppr_options &= ~MSG_EXT_PPR_RTI;
+
+ 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;
+
+ /* 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;
+}
+
+/*
+ * 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) {
+ if ((ahd->bugs & AHD_PACED_NEGTABLE_BUG) != 0)
+ maxoffset = MAX_OFFSET_PACED_BUG;
+ else
+ maxoffset = MAX_OFFSET_PACED;
+ } else
+ maxoffset = MAX_OFFSET_NON_PACED;
+ *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, ahd_neg_type neg_type)
+{
+ u_int auto_negotiate_orig;
+
+ auto_negotiate_orig = tstate->auto_negotiate;
+ if (neg_type == AHD_NEG_ALWAYS) {
+ /*
+ * Force our "current" settings to be
+ * unknown so that unless a bus reset
+ * occurs the need to renegotiate is
+ * recorded persistently.
+ */
+ tinfo->curr.period = AHD_PERIOD_UNKNOWN;
+ tinfo->curr.width = AHD_WIDTH_UNKNOWN;
+ tinfo->curr.offset = AHD_OFFSET_UNKNOWN;
+ }
+ 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
+ || (neg_type == AHD_NEG_IF_NON_ASYNC
+ && (tinfo->goal.offset != 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) {
+ int options;
+
+ printf("%s: target %d synchronous with "
+ "period = 0x%x, offset = 0x%x",
+ ahd_name(ahd), devinfo->target,
+ period, offset);
+ options = 0;
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+ printf("(DT");
+ options++;
+ }
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ printf("%s", options ? "|IU" : "(IU");
+ options++;
+ }
+ if ((ppr_options & MSG_EXT_PPR_RTI) != 0) {
+ printf("%s", options ? "|RTI" : "(RTI");
+ options++;
+ }
+ if ((ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) {
+ printf("%s", options ? "|QAS" : "(QAS");
+ options++;
+ }
+ if (options != 0)
+ printf(")\n");
+ else
+ printf("\n");
+ } else {
+ printf("%s: target %d using "
+ "asynchronous transfers%s\n",
+ ahd_name(ahd), devinfo->target,
+ (ppr_options & MSG_EXT_PPR_QAS_REQ) != 0
+ ? "(QAS)" : "");
+ }
+ }
+ }
+ /*
+ * 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. Also manage the busfree expected flag
+ * from this common routine so that we catch changes due to
+ * WDTR or SDTR messages.
+ */
+ if ((type & AHD_TRANS_CUR) != 0) {
+ if (!paused)
+ ahd_pause(ahd);
+ ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+ if (!paused)
+ ahd_unpause(ahd);
+ if (ahd->msg_type != MSG_TYPE_NONE) {
+ if ((old_ppr & MSG_EXT_PPR_IU_REQ)
+ != (ppr_options & MSG_EXT_PPR_IU_REQ)) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Expecting IU Change busfree\n");
+ }
+#endif
+ ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE
+ | MSG_FLAG_IU_REQ_CHANGED;
+ }
+ if ((old_ppr & MSG_EXT_PPR_IU_REQ) != 0) {
+#ifdef 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;
+ }
+ }
+ }
+
+ update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+ tinfo, AHD_NEG_TO_GOAL);
+
+ if (update_needed && active)
+ 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) {
+ if (!paused)
+ ahd_pause(ahd);
+ ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+ if (!paused)
+ ahd_unpause(ahd);
+ }
+
+ update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+ tinfo, AHD_NEG_TO_GOAL);
+ if (update_needed && active)
+ 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 saved_negoaddr;
+ uint8_t iocell_opts[sizeof(ahd->iocell_opts)];
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ saved_negoaddr = ahd_inb(ahd, NEGOADDR);
+ ahd_outb(ahd, NEGOADDR, devinfo->target);
+ period = tinfo->period;
+ offset = tinfo->offset;
+ memcpy(iocell_opts, ahd->iocell_opts, sizeof(ahd->iocell_opts));
+ ppr_opts = tinfo->ppr_options & (MSG_EXT_PPR_QAS_REQ|MSG_EXT_PPR_DT_REQ
+ |MSG_EXT_PPR_IU_REQ|MSG_EXT_PPR_RTI);
+ con_opts = 0;
+ if (period == 0)
+ period = AHD_SYNCRATE_ASYNC;
+ if (period == AHD_SYNCRATE_160) {
+
+ if ((ahd->bugs & AHD_PACED_NEGTABLE_BUG) != 0) {
+ /*
+ * 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,
+ * Harpoon2A4 allows this to be configurable.
+ *
+ * Harpoon2A4 also assumes at most 2 data bytes per
+ * negotiated REQ/ACK offset. Paced transfers take
+ * 4, so we must adjust our offset.
+ */
+ ppr_opts |= PPROPT_PACE;
+ offset *= 2;
+
+ /*
+ * Harpoon2A assumed that there would be a
+ * fallback rate between 160MHz and 80Mhz,
+ * so 7 is used as the period factor rather
+ * than 8 for 160MHz.
+ */
+ period = AHD_SYNCRATE_REVA_160;
+ }
+ if ((tinfo->ppr_options & MSG_EXT_PPR_PCOMP_EN) == 0)
+ iocell_opts[AHD_PRECOMP_SLEW_INDEX] &=
+ ~AHD_PRECOMP_MASK;
+ } else {
+ /*
+ * Precomp should be disabled for non-paced transfers.
+ */
+ iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_PRECOMP_MASK;
+
+ if ((ahd->features & AHD_NEW_IOCELL_OPTS) != 0
+ && (ppr_opts & MSG_EXT_PPR_DT_REQ) != 0) {
+ /*
+ * Slow down our CRC interval to be
+ * compatible with devices that can't
+ * handle a CRC at full speed.
+ */
+ con_opts |= ENSLOWCRC;
+ }
+ }
+
+ ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_PRECOMP_SLEW);
+ ahd_outb(ahd, ANNEXDAT, iocell_opts[AHD_PRECOMP_SLEW_INDEX]);
+ ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_AMPLITUDE);
+ ahd_outb(ahd, ANNEXDAT, iocell_opts[AHD_AMPLITUDE_INDEX]);
+
+ ahd_outb(ahd, NEGPERIOD, period);
+ ahd_outb(ahd, NEGPPROPTS, ppr_opts);
+ ahd_outb(ahd, NEGOFFSET, offset);
+
+ 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_outb(ahd, NEGOADDR, saved_negoaddr);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * When the transfer settings for a connection change, setup for
+ * negotiation in pending SCBs to effect the change as quickly as
+ * possible. We also cancel any negotiations that are scheduled
+ * for inflight SCBs that have not been started yet.
+ */
+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. We can only safely
+ * clear the negotiation required flag (setting requires the
+ * execution queue to be modified) and this is only possible
+ * if we are not already attempting to select out for this
+ * SCB. For this reason, all callers only call this routine
+ * if we are changing the negotiation settings for the currently
+ * active transaction on the bus.
+ */
+ 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);
+ }
+
+ /*
+ * Force the sequencer to reinitialize the selection for
+ * the command at the head of the execution queue if it
+ * has already been setup. The negotiation changes may
+ * effect whether we select-out with ATN.
+ */
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+ 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);
+}
+
+void
+ahd_print_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ printf("%s:%c:%d:%d: ", ahd_name(ahd), 'A',
+ devinfo->target, devinfo->lun);
+}
+
+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;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Setting up for Parity Error delivery\n");
+#endif
+ 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:%x, MSG_OUT = %x "
+ "SCB flags = %x", SCB_GET_TAG(scb), scb->hscb->control,
+ ahd_inb(ahd, SCB_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;
+ 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;
+ /*
+ * Only use PPR if we have options that need it, even if the device
+ * claims to support it. There might be an expander in the way
+ * that doesn't.
+ */
+ doppr = ppr_options != 0;
+
+ if (!dowide && !dosync && !doppr) {
+ dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+ dosync = tinfo->goal.period != 0;
+ }
+
+ if (!dowide && !dosync && !doppr) {
+ /*
+ * Force async with a WDTR message if we have a wide bus,
+ * or just issue an SDTR with a 0 offset.
+ */
+ if ((ahd->features & AHD_WIDE) != 0)
+ dowide = 1;
+ else
+ dosync = 1;
+
+ if (bootverbose) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Ensuring async\n");
+ }
+ }
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ doppr = 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 (doppr || (dosync && !dowide)) {
+
+ offset = tinfo->goal.offset;
+ ahd_validate_offset(ahd, tinfo, period, &offset,
+ doppr ? tinfo->goal.width
+ : tinfo->curr.width,
+ devinfo->role);
+ if (doppr) {
+ 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)
+{
+ if (offset == 0)
+ period = AHD_ASYNC_XFER_PERIOD;
+ 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;
+ if (offset == 0)
+ period = AHD_ASYNC_XFER_PERIOD;
+ 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");
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("INITIATOR_MSG_OUT");
+ }
+#endif
+ phasemis = bus_phase != P_MESGOUT;
+ if (phasemis) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahd_lookup_phase_entry(bus_phase)
+ ->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);
+#ifdef 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);
+#ifdef 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;
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("INITIATOR_MSG_IN");
+ }
+#endif
+ phasemis = bus_phase != P_MESGIN;
+ if (phasemis) {
+#ifdef 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);
+#ifdef 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) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Asserting ATN for response\n");
+#endif
+ 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_index > index
+ && (ahd->msgout_buf[index] == msgval
+ || ((ahd->msgout_buf[index] & MSG_IDENTIFYFLAG) != 0))
+ && msgval == MSG_IDENTIFYFLAG)
+ 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.offset) {
+ 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);
+ 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:
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("%s: QAS request. SCSISIGI == 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, SCSISIGI));
+#endif
+ ahd->msg_flags |= MSG_FLAG_EXPECT_QASREJ_BUSFREE;
+ /* 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)) {
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, /*full*/TRUE)
+ && tinfo->goal.period <= AHD_SYNCRATE_PACED) {
+ /*
+ * Target may not like our SPI-4 PPR Options.
+ * Attempt to negotiate 80MHz which will turn
+ * off these options.
+ */
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): PPR Rejected. "
+ "Trying simple U160 PPR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ tinfo->goal.period = AHD_SYNCRATE_DT;
+ tinfo->goal.ppr_options &= MSG_EXT_PPR_IU_REQ
+ | MSG_EXT_PPR_QAS_REQ
+ | MSG_EXT_PPR_DT_REQ;
+ } else {
+ /*
+ * 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 if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_IDENTIFYFLAG, TRUE)) {
+ /*
+ * Most likely the device believes that we had
+ * previously negotiated packetized.
+ */
+ ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE
+ | MSG_FLAG_IU_REQ_CHANGED;
+
+ ahd_force_renegotiation(ahd, devinfo);
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_build_transfer_msg(ahd, devinfo);
+ ahd->msgout_index = 0;
+ response = 1;
+ } 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_1 : 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 u_int
+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 u_int
+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);
+ ahd_timer_init(&ahd->stat_timer);
+ ahd->int_coalessing_timer = AHD_INT_COALESSING_TIMER_DEFAULT;
+ ahd->int_coalessing_maxcmds = AHD_INT_COALESSING_MAXCMDS_DEFAULT;
+ ahd->int_coalessing_mincmds = AHD_INT_COALESSING_MINCMDS_DEFAULT;
+ ahd->int_coalessing_threshold = AHD_INT_COALESSING_THRESHOLD_DEFAULT;
+ ahd->int_coalessing_stop_threshold =
+ AHD_INT_COALESSING_STOP_THRESHOLD_DEFAULT;
+
+ if (ahd_platform_alloc(ahd, platform_arg) != 0) {
+ ahd_free(ahd);
+ ahd = NULL;
+ }
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MEMORY) != 0) {
+ printf("%s: scb size = 0x%x, hscb size = 0x%x\n",
+ ahd_name(ahd), (u_int)sizeof(struct scb),
+ (u_int)sizeof(struct hardware_scb));
+ }
+#endif
+ 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);
+ if (ahd->saved_stack != NULL)
+ free(ahd->saved_stack, M_DEVBUF);
+#ifndef __FreeBSD__
+ free(ahd, M_DEVBUF);
+#endif
+ return;
+}
+
+void
+ahd_shutdown(void *arg)
+{
+ struct ahd_softc *ahd;
+
+ ahd = (struct ahd_softc *)arg;
+
+ /*
+ * Stop periodic timer callbacks.
+ */
+ ahd_timer_stop(&ahd->reset_timer);
+ ahd_timer_stop(&ahd->stat_timer);
+
+ /* 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;
+ int i;
+
+ scb_data = &ahd->scb_data;
+ TAILQ_INIT(&scb_data->free_scbs);
+ for (i = 0; i < AHD_NUM_TARGETS * AHD_NUM_LUNS_NONPKT; i++)
+ LIST_INIT(&scb_data->free_scb_lists[i]);
+ LIST_INIT(&scb_data->any_dev_free_scb_list);
+ 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;
+ }
+#ifdef 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;
+ }
+
+ /*
+ * Note that we were successfull
+ */
+ return (0);
+
+error_exit:
+
+ return (ENOMEM);
+}
+
+static struct scb *
+ahd_find_scb_by_tag(struct ahd_softc *ahd, u_int tag)
+{
+ struct scb *scb;
+
+ /*
+ * Look on the pending list.
+ */
+ LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+ if (SCB_GET_TAG(scb) == tag)
+ return (scb);
+ }
+
+ /*
+ * Then on all of the collision free lists.
+ */
+ TAILQ_FOREACH(scb, &ahd->scb_data.free_scbs, links.tqe) {
+ struct scb *list_scb;
+
+ list_scb = scb;
+ do {
+ if (SCB_GET_TAG(list_scb) == tag)
+ return (list_scb);
+ list_scb = LIST_NEXT(list_scb, collision_links);
+ } while (list_scb);
+ }
+
+ /*
+ * And finally on the generic free list.
+ */
+ LIST_FOREACH(scb, &ahd->scb_data.any_dev_free_scb_list, links.le) {
+ if (SCB_GET_TAG(scb) == tag)
+ return (scb);
+ }
+
+ return (NULL);
+}
+
+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));
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: Setting up iocell workaround\n", ahd_name(ahd));
+#endif
+ 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);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: iocell first selection\n", ahd_name(ahd));
+#endif
+ if ((sblkctl & ENAB40) != 0) {
+ ahd_outb(ahd, DSPDATACTL,
+ ahd_inb(ahd, DSPDATACTL) & ~BYPASSENAB);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: BYPASS now disabled\n", ahd_name(ahd));
+#endif
+ }
+ ahd_outb(ahd, SIMODE0, ahd_inb(ahd, SIMODE0) & ~(ENSELDO|ENSELDI));
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/*************************** SCB Management ***********************************/
+static void
+ahd_add_col_list(struct ahd_softc *ahd, struct scb *scb, u_int col_idx)
+{
+ struct scb_list *free_list;
+ struct scb_tailq *free_tailq;
+ struct scb *first_scb;
+
+ scb->flags |= SCB_ON_COL_LIST;
+ AHD_SET_SCB_COL_IDX(scb, col_idx);
+ free_list = &ahd->scb_data.free_scb_lists[col_idx];
+ free_tailq = &ahd->scb_data.free_scbs;
+ first_scb = LIST_FIRST(free_list);
+ if (first_scb != NULL) {
+ LIST_INSERT_AFTER(first_scb, scb, collision_links);
+ } else {
+ LIST_INSERT_HEAD(free_list, scb, collision_links);
+ TAILQ_INSERT_TAIL(free_tailq, scb, links.tqe);
+ }
+}
+
+static void
+ahd_rem_col_list(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct scb_list *free_list;
+ struct scb_tailq *free_tailq;
+ struct scb *first_scb;
+ u_int col_idx;
+
+ scb->flags &= ~SCB_ON_COL_LIST;
+ col_idx = AHD_GET_SCB_COL_IDX(ahd, scb);
+ free_list = &ahd->scb_data.free_scb_lists[col_idx];
+ free_tailq = &ahd->scb_data.free_scbs;
+ first_scb = LIST_FIRST(free_list);
+ if (first_scb == scb) {
+ struct scb *next_scb;
+
+ /*
+ * Maintain order in the collision free
+ * lists for fairness if this device has
+ * other colliding tags active.
+ */
+ next_scb = LIST_NEXT(scb, collision_links);
+ if (next_scb != NULL) {
+ TAILQ_INSERT_AFTER(free_tailq, scb,
+ next_scb, links.tqe);
+ }
+ TAILQ_REMOVE(free_tailq, scb, links.tqe);
+ }
+ LIST_REMOVE(scb, collision_links);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+struct scb *
+ahd_get_scb(struct ahd_softc *ahd, u_int col_idx)
+{
+ struct scb *scb;
+ int tries;
+
+ tries = 0;
+look_again:
+ TAILQ_FOREACH(scb, &ahd->scb_data.free_scbs, links.tqe) {
+ if (AHD_GET_SCB_COL_IDX(ahd, scb) != col_idx) {
+ ahd_rem_col_list(ahd, scb);
+ goto found;
+ }
+ }
+ if ((scb = LIST_FIRST(&ahd->scb_data.any_dev_free_scb_list)) == NULL) {
+
+ if (tries++ != 0)
+ return (NULL);
+ ahd_alloc_scbs(ahd);
+ goto look_again;
+ }
+ LIST_REMOVE(scb, links.le);
+ if (col_idx != AHD_NEVER_COL_IDX
+ && (scb->col_scb != NULL)
+ && (scb->col_scb->flags & SCB_ACTIVE) == 0) {
+ LIST_REMOVE(scb->col_scb, links.le);
+ ahd_add_col_list(ahd, scb->col_scb, col_idx);
+ }
+found:
+ scb->flags |= SCB_ACTIVE;
+ return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+void
+ahd_free_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+
+ /* Clean up for the next user */
+ scb->flags = SCB_FLAG_NONE;
+ scb->hscb->control = 0;
+ ahd->scb_data.scbindex[scb->hscb->tag] = NULL;
+
+ if (scb->col_scb == NULL) {
+
+ /*
+ * No collision possible. Just free normally.
+ */
+ LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+ scb, links.le);
+ } else if ((scb->col_scb->flags & SCB_ON_COL_LIST) != 0) {
+
+ /*
+ * The SCB we might have collided with is on
+ * a free collision list. Put both SCBs on
+ * the generic list.
+ */
+ ahd_rem_col_list(ahd, scb->col_scb);
+ LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+ scb, links.le);
+ LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+ scb->col_scb, links.le);
+ } else if ((scb->col_scb->flags
+ & (SCB_PACKETIZED|SCB_ACTIVE)) == SCB_ACTIVE
+ && (scb->col_scb->hscb->control & TAG_ENB) != 0) {
+
+ /*
+ * The SCB we might collide with on the next allocation
+ * is still active in a non-packetized, tagged, context.
+ * Put us on the SCB collision list.
+ */
+ ahd_add_col_list(ahd, scb,
+ AHD_GET_SCB_COL_IDX(ahd, scb->col_scb));
+ } else {
+ /*
+ * The SCB we might collide with on the next allocation
+ * is either active in a packetized context, or free.
+ * Since we can't collide, put this SCB on the generic
+ * free list.
+ */
+ LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+ scb, links.le);
+ }
+
+ ahd_platform_scb_free(ahd, scb);
+}
+
+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++) {
+ u_int col_tag;
+
+ 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_FLAG_NONE;
+#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);
+ col_tag = scb_data->numscbs ^ 0x100;
+ next_scb->col_scb = ahd_find_scb_by_tag(ahd, col_tag);
+ if (next_scb->col_scb != NULL)
+ next_scb->col_scb->col_scb = next_scb;
+ ahd_free_scb(ahd, next_scb);
+ 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)
+{
+ uint8_t *base_vaddr;
+ uint8_t *next_vaddr;
+ bus_addr_t next_baddr;
+ size_t driver_data_size;
+ int i;
+ int error;
+ u_int warn_user;
+ uint8_t current_sensing;
+ uint8_t fstat;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+ ahd->stack_size = ahd_probe_stack_size(ahd);
+ ahd->saved_stack = malloc(ahd->stack_size * sizeof(uint16_t),
+ M_DEVBUF, M_NOWAIT);
+ if (ahd->saved_stack == NULL)
+ return (ENOMEM);
+
+ /*
+ * 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
+ if ((ahd_debug & AHD_DEBUG_SEQUENCER) != 0)
+ 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)
+ + sizeof(struct hardware_scb);
+ 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 **)&base_vaddr,
+ 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,
+ base_vaddr, driver_data_size, ahd_dmamap_cb,
+ &ahd->shared_data_busaddr, /*flags*/0);
+ ahd->qoutfifo = (uint16_t *)base_vaddr;
+ next_vaddr = (uint8_t *)&ahd->qoutfifo[AHD_QOUT_SIZE];
+ next_baddr = ahd->shared_data_busaddr + AHD_QOUT_SIZE*sizeof(uint16_t);
+ if ((ahd->features & AHD_TARGETMODE) != 0) {
+ ahd->targetcmds = (struct target_cmd *)next_vaddr;
+ next_vaddr += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+ next_baddr += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+ }
+
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+ ahd->overrun_buf = next_vaddr;
+ next_vaddr += PKT_OVERRUN_BUFSIZE;
+ next_baddr += PKT_OVERRUN_BUFSIZE;
+ }
+
+ /*
+ * We need one SCB to serve as the "next SCB". Since the
+ * tag identifier in this SCB will never be used, there is
+ * no point in using a valid HSCB tag from an SCB pulled from
+ * the standard free pool. So, we allocate this "sentinel"
+ * specially from the DMA safe memory chunk used for the QOUTFIFO.
+ */
+ ahd->next_queued_hscb = (struct hardware_scb *)next_vaddr;
+ ahd->next_queued_hscb->hscb_busaddr = next_baddr;
+
+ 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;
+
+ /*
+ * Before committing these settings to the chip, give
+ * the OSM one last chance to modify our configuration.
+ */
+ ahd_platform_init(ahd);
+
+ /* Bring up the chip. */
+ ahd_chip_init(ahd);
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+ 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);
+ ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US,
+ ahd_stat_timer, 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 wait;
+ 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));
+
+ /*
+ * Return HS_MAILBOX to its default value.
+ */
+ ahd->hs_mailbox = 0;
+ ahd_outb(ahd, HS_MAILBOX, 0);
+
+ /* 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);
+
+ /*
+ * Now that termination is set, 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);
+
+ /* Clear any false bus resets due to the transceivers settling */
+ ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+
+ /* 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);
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+ ahd_outb(ahd, OPTIONMODE, AUTOACKEN|AUTO_MSGOUT_DE);
+ } else {
+ ahd_outb(ahd, OPTIONMODE, AUTOACKEN|BUSFREEREV|AUTO_MSGOUT_DE);
+ }
+ ahd_outb(ahd, SCSCHKN, CURRFIFODEF|WIDERESEN);
+ 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);
+
+ if ((ahd->bugs & AHD_LQOOVERRUN_BUG) != 0)
+ ahd_outb(ahd, LQOSCSCTL, LQONOCHKOVER);
+
+ /*
+ * Tweak IOCELL settings.
+ */
+ if ((ahd->flags & AHD_HP_BOARD) != 0) {
+ for (i = 0; i < NUMDSPS; i++) {
+ ahd_outb(ahd, DSPSELECT, i);
+ ahd_outb(ahd, WRTBIASCTL, WRTBIASCTL_HP_DEFAULT);
+ }
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: WRTBIASCTL now 0x%x\n", ahd_name(ahd),
+ WRTBIASCTL_HP_DEFAULT);
+#endif
+ }
+ 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 handlers.
+ */
+ ahd_outw(ahd, INTVEC1_ADDR, ahd_resolve_seqaddr(ahd, LABEL_seq_isr));
+ ahd_outw(ahd, INTVEC2_ADDR, ahd_resolve_seqaddr(ahd, LABEL_timer_isr));
+
+ /*
+ * Setup SCB Offset registers.
+ */
+ if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0) {
+ ahd_outb(ahd, LUNPTR, offsetof(struct hardware_scb,
+ pkt_long_lun));
+ } else {
+ 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));
+ 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));
+ if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0) {
+ ahd_outb(ahd, LUNLEN,
+ sizeof(ahd->next_queued_hscb->pkt_long_lun) - 1);
+ } else {
+ ahd_outb(ahd, LUNLEN, sizeof(ahd->next_queued_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);
+ /* Initialize the negotiation table. */
+ if ((ahd->features & AHD_NEW_IOCELL_OPTS) == 0) {
+ /*
+ * 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_PER_DEV0);
+ for (i = 0; i < AHD_NUM_PER_DEV_ANNEXCOLS; i++)
+ ahd_outb(ahd, ANNEXDAT, 0);
+ }
+ }
+ for (target = 0; target < AHD_NUM_TARGETS; target++) {
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ target, &tstate);
+ ahd_compile_devinfo(&devinfo, ahd->our_id,
+ target, CAM_LUN_WILDCARD,
+ 'A', ROLE_INITIATOR);
+ ahd_update_neg_table(ahd, &devinfo, &tinfo->curr);
+ }
+
+ ahd_outb(ahd, CLRSINT3, NTRAMPERR|OSRAMPERR);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+
+ /*
+ * 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;
+ ahd->qoutfifonext_valid_tag = QOUTFIFO_ENTRY_VALID_LE;
+ ahd_outb(ahd, QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID >> 8);
+ for (i = 0; i < AHD_QOUT_SIZE; i++)
+ ahd->qoutfifo[i] = 0;
+ 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));
+ }
+
+ /*
+ * 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_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);
+
+ /*
+ * Default to coalessing disabled.
+ */
+ ahd_outw(ahd, INT_COALESSING_CMDCOUNT, 0);
+ ahd_outw(ahd, CMDS_PENDING, 0);
+ ahd_update_coalessing_values(ahd, ahd->int_coalessing_timer,
+ ahd->int_coalessing_maxcmds,
+ ahd->int_coalessing_mincmds);
+ ahd_enable_coalessing(ahd, FALSE);
+
+ 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;
+
+ 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_HOLD_MCS
+ | MSG_EXT_PPR_IU_REQ
+ | MSG_EXT_PPR_QAS_REQ
+ | MSG_EXT_PPR_DT_REQ;
+ if ((ahd->features & AHD_RTI) != 0)
+ tinfo->user.ppr_options |= MSG_EXT_PPR_RTI;
+
+ 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);
+ }
+ 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_ASYNC_XFER_PERIOD;
+ }
+#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_HOLD_MCS
+ | MSG_EXT_PPR_IU_REQ;
+ if ((ahd->features & AHD_RTI) != 0)
+ user_tinfo->ppr_options |= MSG_EXT_PPR_RTI;
+ }
+
+ 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);
+ }
+
+ 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);
+}
+
+void
+ahd_update_coalessing_values(struct ahd_softc *ahd, u_int timer, u_int maxcmds,
+ u_int mincmds)
+{
+ if (timer > AHD_TIMER_MAX_US)
+ timer = AHD_TIMER_MAX_US;
+ ahd->int_coalessing_timer = timer;
+
+ if (maxcmds > AHD_INT_COALESSING_MAXCMDS_MAX)
+ maxcmds = AHD_INT_COALESSING_MAXCMDS_MAX;
+ if (mincmds > AHD_INT_COALESSING_MINCMDS_MAX)
+ mincmds = AHD_INT_COALESSING_MINCMDS_MAX;
+ ahd->int_coalessing_maxcmds = maxcmds;
+ ahd_outw(ahd, INT_COALESSING_TIMER, timer / AHD_TIMER_US_PER_TICK);
+ ahd_outb(ahd, INT_COALESSING_MAXCMDS, -maxcmds);
+ ahd_outb(ahd, INT_COALESSING_MINCMDS, -mincmds);
+}
+
+void
+ahd_enable_coalessing(struct ahd_softc *ahd, int enable)
+{
+
+ ahd->hs_mailbox &= ~ENINT_COALESS;
+ if (enable)
+ ahd->hs_mailbox |= ENINT_COALESS;
+ ahd_outb(ahd, HS_MAILBOX, ahd->hs_mailbox);
+ ahd_flush_device_writes(ahd);
+ ahd_run_qoutfifo(ahd);
+}
+
+/*
+ * 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)
+{
+ u_int intstat;
+ u_int maxloops;
+ int paused;
+
+ maxloops = 1000;
+ ahd->flags |= AHD_ALL_INTERRUPTS;
+ intstat = 0;
+ paused = FALSE;
+ do {
+ struct scb *waiting_scb;
+
+ if (paused)
+ ahd_unpause(ahd);
+ ahd_intr(ahd);
+ ahd_pause(ahd);
+ paused = TRUE;
+ ahd_clear_critical_section(ahd);
+ if ((ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) == 0)
+ ahd_outb(ahd, SCSISEQ0,
+ ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+ /*
+ * In the non-packetized case, the sequencer (for Rev A),
+ * relies on ENSELO remaining set after SELDO. The hardware
+ * auto-clears ENSELO in the packetized case.
+ */
+ waiting_scb = ahd_lookup_scb(ahd,
+ ahd_inw(ahd, WAITING_TID_HEAD));
+ if (waiting_scb != NULL
+ && (waiting_scb->flags & SCB_PACKETIZED) == 0
+ && (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) != 0)
+ ahd_outb(ahd, SCSISEQ0,
+ ahd_inb(ahd, SCSISEQ0) | ENSELO);
+
+ if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0)
+ break;
+ } while (--maxloops
+ && (((intstat = ahd_inb(ahd, INTSTAT)) & INT_PEND) != 0
+ || (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO))));
+ if (maxloops == 0) {
+ printf("Infinite interrupt loop, INTSTAT = %x",
+ ahd_inb(ahd, INTSTAT));
+ }
+
+ ahd_flush_qoutfifo(ahd);
+
+ 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_NONPKT; j++) {
+ u_int tcl;
+
+ tcl = BUILD_TCL_RAW(i, 'A', 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)
+{
+ /*
+ * Index to the SCB that contains the busy entry.
+ */
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ *saved_scbid = ahd_get_scbptr(ahd);
+ ahd_set_scbptr(ahd, TCL_LUN(tcl)
+ | ((TCL_TARGET_OFFSET(tcl) & 0xC) << 4));
+
+ /*
+ * 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) & 0x3) << 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);
+}
+
+/************************** 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;
+ ahd_mode_state saved_modes;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+ 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);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+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_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);
+}
+
+void
+ahd_reset_cmds_pending(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ ahd_mode_state saved_modes;
+ u_int pending_cmds;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+ /*
+ * Don't count any commands as outstanding that the
+ * sequencer has already marked for completion.
+ */
+ ahd_flush_qoutfifo(ahd);
+
+ pending_cmds = 0;
+ LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+ pending_cmds++;
+ }
+ ahd_outw(ahd, CMDS_PENDING, pending_cmds - ahd_qinfifo_count(ahd));
+ ahd_restore_modes(ahd, saved_modes);
+ ahd->flags &= ~AHD_UPDATE_PEND_CMDS;
+}
+
+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_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_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);
+ 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");
+ }
+ if (action == SEARCH_COMPLETE
+ || action == SEARCH_REMOVE)
+ ahd_outw(ahd, CMDS_PENDING, ahd_inw(ahd, CMDS_PENDING) - found);
+ 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;
+ ahd_mode_state saved_modes;
+
+ /* restore these when we're done */
+ active_scb = ahd_get_scbptr(ahd);
+ saved_modes = ahd_save_modes(ahd);
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ 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_RAW(i, 'A', 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));
+ }
+ }
+ }
+
+ /*
+ * Don't abort commands that have already completed,
+ * but haven't quite made it up to the host yet.
+ */
+ ahd_flush_qoutfifo(ahd);
+
+ /*
+ * 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_restore_modes(ahd, saved_modes);
+ ahd_platform_abort_scbs(ahd, target, channel, lun, tag, role, status);
+ ahd->flags |= AHD_UPDATE_PEND_CMDS;
+ 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) & ~(ENSELO|ENARBO|SCSIRSTO);
+ ahd_outb(ahd, SCSISEQ0, scsiseq | SCSIRSTO);
+ ahd_delay(AHD_BUSRESET_DELAY);
+ /* Turn off the bus reset */
+ ahd_outb(ahd, SCSISEQ0, scsiseq);
+ 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_delay(AHD_BUSRESET_DELAY);
+ 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);
+}
+
+int
+ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
+{
+ struct ahd_devinfo devinfo;
+ u_int initiator;
+ u_int target;
+ u_int max_scsiid;
+ int found;
+ u_int fifo;
+ u_int next_fifo;
+
+ 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);
+
+#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);
+
+ /*
+ * Disable selections so no automatic hardware
+ * functions will modify chip state.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+ ahd_outb(ahd, SCSISEQ1, 0);
+
+ /*
+ * Safely shut down our DMA engines. Always start with
+ * the FIFO that is not currently active (if any are
+ * actively connected).
+ */
+ next_fifo = fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO;
+ if (next_fifo > CURRFIFO_1)
+ /* If disconneced, arbitrarily start with FIFO1. */
+ next_fifo = fifo = 0;
+ do {
+ next_fifo ^= CURRFIFO_1;
+ ahd_set_modes(ahd, next_fifo, next_fifo);
+ ahd_outb(ahd, DFCNTRL,
+ ahd_inb(ahd, DFCNTRL) & ~(SCSIEN|HDMAEN));
+ while ((ahd_inb(ahd, DFCNTRL) & HDMAENACK) != 0)
+ ahd_delay(10);
+ /*
+ * Set CURRFIFO to the now inactive channel.
+ */
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, DFFSTAT, next_fifo);
+ } while (next_fifo != fifo);
+ /*
+ * Reset the bus if we are initiating this reset
+ */
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, SIMODE1,
+ ahd_inb(ahd, SIMODE1) & ~(ENBUSFREE|ENSCSIRST|ENBUSFREE));
+ 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);
+
+ /*
+ * Cleanup anything left in the FIFOs.
+ */
+ ahd_clear_fifo(ahd, 0);
+ ahd_clear_fifo(ahd, 1);
+
+ /*
+ * 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);
+ }
+ }
+
+#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);
+ ahd_restart(ahd);
+ /*
+ * 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.
+ */
+ if ((ahd->flags & AHD_RESET_POLL_ACTIVE) == 0) {
+ ahd->flags |= AHD_RESET_POLL_ACTIVE;
+ 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_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);
+ ahd_pause(ahd);
+ ahd_update_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
+ if ((ahd_inb(ahd, SSTAT1) & SCSIRSTI) != 0) {
+ ahd_timer_reset(&ahd->reset_timer, AHD_RESET_POLL_US,
+ ahd_reset_poll, ahd);
+ ahd_unpause(ahd);
+ ahd_unlock(ahd, &s);
+ ahd_list_unlock(&l);
+ return;
+ }
+
+ /* 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));
+ ahd_unpause(ahd);
+ ahd->flags &= ~AHD_RESET_POLL_ACTIVE;
+ ahd_unlock(ahd, &s);
+ ahd_release_simq(ahd);
+ ahd_list_unlock(&l);
+}
+
+/**************************** Statistics Processing ***************************/
+static void
+ahd_stat_timer(void *arg)
+{
+ struct ahd_softc *ahd;
+ u_long l;
+ u_long s;
+ int enint_coal;
+
+ ahd_list_lock(&l);
+ ahd = ahd_find_softc((struct ahd_softc *)arg);
+ if (ahd == NULL) {
+ printf("ahd_stat_timer: Instance %p no longer exists\n", arg);
+ ahd_list_unlock(&l);
+ return;
+ }
+ ahd_lock(ahd, &s);
+
+ enint_coal = ahd->hs_mailbox & ENINT_COALESS;
+ if (ahd->cmdcmplt_total > ahd->int_coalessing_threshold)
+ enint_coal |= ENINT_COALESS;
+ else if (ahd->cmdcmplt_total < ahd->int_coalessing_stop_threshold)
+ enint_coal &= ~ENINT_COALESS;
+
+ if (enint_coal != (ahd->hs_mailbox & ENINT_COALESS)) {
+ ahd_enable_coalessing(ahd, enint_coal);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_INT_COALESSING) != 0)
+ printf("%s: Interrupt coalessing "
+ "now %sabled. Cmds %d\n",
+ ahd_name(ahd),
+ (enint_coal & ENINT_COALESS) ? "en" : "dis",
+ ahd->cmdcmplt_total,
+ ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket]);
+#endif
+ }
+
+ ahd->cmdcmplt_bucket = (ahd->cmdcmplt_bucket+1) & (AHD_STAT_BUCKETS-1);
+ ahd->cmdcmplt_total -= ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket];
+ ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket] = 0;
+ ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US,
+ ahd_stat_timer, ahd);
+ ahd_unlock(ahd, &s);
+ 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;
+
+ /* 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);
+ /* 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);
+ 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);
+#ifdef 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;
+#ifdef 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,
+ AHD_NEG_IF_NON_ASYNC);
+ }
+ 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);
+ /*
+ * Ensure we have enough time to actually
+ * retrieve the sense.
+ */
+ ahd_scb_timer_reset(scb, 5 * 1000000);
+ 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 != 7
+#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->overrun_buf - (uint8_t *)ahd->qoutfifo) / 256;
+ download_consts[SCB_TRANSFER_SIZE] = SCB_TRANSFER_SIZE_1BYTE_LUN;
+ if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0)
+ download_consts[SCB_TRANSFER_SIZE] = SCB_TRANSFER_SIZE_FULL_LUN;
+ 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;
+ }
+}
+
+static int
+ahd_probe_stack_size(struct ahd_softc *ahd)
+{
+ int last_probe;
+
+ last_probe = 0;
+ while (1) {
+ int i;
+
+ /*
+ * We avoid using 0 as a pattern to avoid
+ * confusion if the stack implementation
+ * "back-fills" with zeros when "poping'
+ * entries.
+ */
+ for (i = 1; i <= last_probe+1; i++) {
+ ahd_outb(ahd, STACK, i & 0xFF);
+ ahd_outb(ahd, STACK, (i >> 8) & 0xFF);
+ }
+
+ /* Verify */
+ for (i = last_probe+1; i > 0; i--) {
+ u_int stack_entry;
+
+ stack_entry = ahd_inb(ahd, STACK)
+ |(ahd_inb(ahd, STACK) << 8);
+ if (stack_entry != i)
+ goto sized;
+ }
+ last_probe++;
+ }
+sized:
+ return (last_probe);
+}
+
+void
+ahd_dump_all_cards_state()
+{
+ struct ahd_softc *list_ahd;
+
+ TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+ ahd_dump_card_state(list_ahd);
+ }
+}
+
+int
+ahd_print_register(ahd_reg_parse_entry_t *table, u_int num_entries,
+ const char *name, u_int address, u_int value,
+ u_int *cur_column, u_int wrap_point)
+{
+ int printed;
+ u_int printed_mask;
+
+ if (cur_column != NULL && *cur_column >= wrap_point) {
+ printf("\n");
+ *cur_column = 0;
+ }
+ printed = printf("%s[0x%x]", name, value);
+ if (table == NULL) {
+ printed += printf(" ");
+ *cur_column += printed;
+ return (printed);
+ }
+ printed_mask = 0;
+ while (printed_mask != 0xFF) {
+ int entry;
+
+ for (entry = 0; entry < num_entries; entry++) {
+ if (((value & table[entry].mask)
+ != table[entry].value)
+ || ((printed_mask & table[entry].mask)
+ == table[entry].mask))
+ continue;
+
+ printed += printf("%s%s",
+ printed_mask == 0 ? ":(" : "|",
+ table[entry].name);
+ printed_mask |= table[entry].mask;
+
+ break;
+ }
+ if (entry >= num_entries)
+ break;
+ }
+ if (printed_mask != 0)
+ printed += printf(") ");
+ else
+ printed += printf(" ");
+ if (cur_column != NULL)
+ *cur_column += printed;
+ return (printed);
+}
+
+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 saved_scb_index;
+ u_int cur_col;
+ 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(">>>>>>>>>>>>>>>>>> Dump Card State Begins <<<<<<<<<<<<<<<<<\n"
+ "%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));
+ if (paused)
+ printf("Card was paused\n");
+ /*
+ * Mode independent registers.
+ */
+ cur_col = 0;
+ ahd_hs_mailbox_print(ahd_inb(ahd, LOCAL_HS_MAILBOX), &cur_col, 50);
+ ahd_intctl_print(ahd_inb(ahd, INTCTL), &cur_col, 50);
+ ahd_seqintstat_print(ahd_inb(ahd, SEQINTSTAT), &cur_col, 50);
+ ahd_saved_mode_print(ahd_inb(ahd, SAVED_MODE), &cur_col, 50);
+ ahd_dffstat_print(ahd_inb(ahd, DFFSTAT), &cur_col, 50);
+ ahd_scsisigi_print(ahd_inb(ahd, SCSISIGI), &cur_col, 50);
+ ahd_scsiphase_print(ahd_inb(ahd, SCSIPHASE), &cur_col, 50);
+ ahd_scsibus_print(ahd_inb(ahd, SCSIBUS), &cur_col, 50);
+ ahd_lastphase_print(ahd_inb(ahd, LASTPHASE), &cur_col, 50);
+ ahd_scsiseq0_print(ahd_inb(ahd, SCSISEQ0), &cur_col, 50);
+ ahd_scsiseq1_print(ahd_inb(ahd, SCSISEQ1), &cur_col, 50);
+ ahd_seqctl0_print(ahd_inb(ahd, SEQCTL0), &cur_col, 50);
+ ahd_seqintctl_print(ahd_inb(ahd, SEQINTCTL), &cur_col, 50);
+ ahd_seq_flags_print(ahd_inb(ahd, SEQ_FLAGS), &cur_col, 50);
+ ahd_seq_flags2_print(ahd_inb(ahd, SEQ_FLAGS2), &cur_col, 50);
+ ahd_sstat0_print(ahd_inb(ahd, SSTAT0), &cur_col, 50);
+ ahd_sstat1_print(ahd_inb(ahd, SSTAT1), &cur_col, 50);
+ ahd_sstat2_print(ahd_inb(ahd, SSTAT2), &cur_col, 50);
+ ahd_sstat3_print(ahd_inb(ahd, SSTAT3), &cur_col, 50);
+ ahd_perrdiag_print(ahd_inb(ahd, PERRDIAG), &cur_col, 50);
+ ahd_simode1_print(ahd_inb(ahd, SIMODE1), &cur_col, 50);
+ ahd_lqistat0_print(ahd_inb(ahd, LQISTAT0), &cur_col, 50);
+ ahd_lqistat1_print(ahd_inb(ahd, LQISTAT1), &cur_col, 50);
+ ahd_lqistat2_print(ahd_inb(ahd, LQISTAT2), &cur_col, 50);
+ ahd_lqostat0_print(ahd_inb(ahd, LQOSTAT0), &cur_col, 50);
+ ahd_lqostat1_print(ahd_inb(ahd, LQOSTAT1), &cur_col, 50);
+ ahd_lqostat2_print(ahd_inb(ahd, LQOSTAT2), &cur_col, 50);
+ printf("\n");
+ printf("\nSCB Count = %d CMDS_PENDING = %d LASTSCB 0x%x "
+ "CURRSCB 0x%x NEXTSCB 0x%x\n",
+ ahd->scb_data.numscbs, ahd_inw(ahd, CMDS_PENDING),
+ ahd_inw(ahd, LASTSCB), ahd_inw(ahd, CURRSCB),
+ ahd_inw(ahd, NEXTSCB));
+ cur_col = 0;
+ /* QINFIFO */
+ ahd_search_qinfifo(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_UNKNOWN, /*status*/0, SEARCH_PRINT);
+ saved_scb_index = ahd_get_scbptr(ahd);
+ printf("Pending list:");
+ i = 0;
+ LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+ if (i++ > AHD_SCB_MAX)
+ break;
+ cur_col = printf("\n%3d ", SCB_GET_TAG(scb));
+ ahd_set_scbptr(ahd, SCB_GET_TAG(scb));
+ ahd_scb_control_print(ahd_inb(ahd, SCB_CONTROL), &cur_col, 60);
+ ahd_scb_scsiid_print(ahd_inb(ahd, SCB_SCSIID), &cur_col, 60);
+ ahd_scb_tag_print(ahd_inb(ahd, SCB_TAG), &cur_col, 60);
+ }
+ printf("\nTotal %d\n", i);
+
+ printf("Kernel Free SCB list: ");
+ i = 0;
+ TAILQ_FOREACH(scb, &ahd->scb_data.free_scbs, links.tqe) {
+ struct scb *list_scb;
+
+ list_scb = scb;
+ do {
+ printf("%d ", SCB_GET_TAG(list_scb));
+ list_scb = LIST_NEXT(list_scb, collision_links);
+ } while (list_scb && i++ < AHD_SCB_MAX);
+ }
+
+ LIST_FOREACH(scb, &ahd->scb_data.any_dev_free_scb_list, links.le) {
+ 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");
+ ahd_set_scbptr(ahd, saved_scb_index);
+ dffstat = ahd_inb(ahd, DFFSTAT);
+ for (i = 0; i < 2; i++) {
+#ifdef AHD_DEBUG
+ struct scb *fifo_scb;
+#endif
+ u_int fifo_scbptr;
+
+ ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
+ fifo_scbptr = ahd_get_scbptr(ahd);
+ printf("\n%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));
+ cur_col = 0;
+ ahd_seqimode_print(ahd_inb(ahd, SEQIMODE), &cur_col, 50);
+ ahd_seqintsrc_print(ahd_inb(ahd, SEQINTSRC), &cur_col, 50);
+ ahd_dfcntrl_print(ahd_inb(ahd, DFCNTRL), &cur_col, 50);
+ ahd_dfstatus_print(ahd_inb(ahd, DFSTATUS), &cur_col, 50);
+ ahd_sg_cache_shadow_print(ahd_inb(ahd, SG_CACHE_SHADOW),
+ &cur_col, 50);
+ ahd_sg_state_print(ahd_inb(ahd, SG_STATE), &cur_col, 50);
+ ahd_dffsxfrctl_print(ahd_inb(ahd, DFFSXFRCTL), &cur_col, 50);
+ ahd_soffcnt_print(ahd_inb(ahd, SOFFCNT), &cur_col, 50);
+ ahd_mdffstat_print(ahd_inb(ahd, MDFFSTAT), &cur_col, 50);
+ if (cur_col > 50) {
+ printf("\n");
+ cur_col = 0;
+ }
+ cur_col += printf("SHADDR = 0x%x%x, SHCNT = 0x%x ",
+ ahd_inl(ahd, SHADDR+4),
+ ahd_inl(ahd, SHADDR),
+ (ahd_inb(ahd, SHCNT)
+ | (ahd_inb(ahd, SHCNT + 1) << 8)
+ | (ahd_inb(ahd, SHCNT + 2) << 16)));
+ if (cur_col > 50) {
+ printf("\n");
+ cur_col = 0;
+ }
+ cur_col += printf("HADDR = 0x%x%x, HCNT = 0x%x ",
+ ahd_inl(ahd, HADDR+4),
+ ahd_inl(ahd, HADDR),
+ (ahd_inb(ahd, HCNT)
+ | (ahd_inb(ahd, HCNT + 1) << 8)
+ | (ahd_inb(ahd, HCNT + 2) << 16)));
+ ahd_ccsgctl_print(ahd_inb(ahd, CCSGCTL), &cur_col, 50);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_SG) != 0) {
+ fifo_scb = ahd_lookup_scb(ahd, fifo_scbptr);
+ if (fifo_scb != NULL)
+ ahd_dump_sglist(fifo_scb);
+ }
+#endif
+ }
+ printf("\nLQIN: ");
+ for (i = 0; i < 20; i++)
+ printf("0x%x ", ahd_inb(ahd, LQIN + i));
+ printf("\n");
+ 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_simode0_print(ahd_inb(ahd, SIMODE0), &cur_col, 50);
+ printf("\n");
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+ cur_col = 0;
+ ahd_ccscbctl_print(ahd_inb(ahd, CCSCBCTL), &cur_col, 50);
+ printf("\n");
+ 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 < ahd->stack_size; i++) {
+ ahd->saved_stack[i] =
+ ahd_inb(ahd, STACK)|(ahd_inb(ahd, STACK) << 8);
+ printf(" 0x%x", ahd->saved_stack[i]);
+ }
+ for (i = ahd->stack_size-1; i >= 0; i--) {
+ ahd_outb(ahd, STACK, ahd->saved_stack[i] & 0xFF);
+ ahd_outb(ahd, STACK, (ahd->saved_stack[i] >> 8) & 0xFF);
+ }
+ printf("\n<<<<<<<<<<<<<<<<< Dump Card State Ends >>>>>>>>>>>>>>>>>>\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;
+ u_int saved_scb_index;
+ int i;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ saved_scb_index = ahd_get_scbptr(ahd);
+ 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_set_scbptr(ahd, saved_scb_index);
+ 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)
+{
+ /*
+ * We should be able to determine the SEEPROM type
+ * from the flexport logic, but unfortunately not
+ * all implementations have this logic and there is
+ * no programatic method for determining if the logic
+ * is present.
+ */
+ return (1);
+#if 0
+ 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);
+#endif
+}
+
+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 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;
+#ifdef 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.
+ */
+#ifdef 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/drivers/scsi/aic7xxx/aic79xx_inline.h b/drivers/scsi/aic7xxx/aic79xx_inline.h
new file mode 100644
index 000000000000..d14d76e3db9c
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_inline.h
@@ -0,0 +1,951 @@
+/*
+ * Inline routines 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_inline.h#40 $
+ *
+ * $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("%s: Setting mode 0x%x\n", ahd_name(ahd),
+ 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) {
+ if ((ahd->flags & AHD_UPDATE_PEND_CMDS) != 0)
+ ahd_reset_cmds_pending(ahd);
+ 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);
+}
+
+/*********************** 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_htole32(addr & 0xFFFFFFFF);
+ 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. */
+ scb->crc_retry_count = 0;
+ if ((scb->flags & SCB_PACKETIZED) != 0) {
+ /* XXX what about ACA?? It is type 4, but TAG_TYPE == 0x3. */
+ scb->hscb->task_attribute= scb->hscb->control & SCB_TAG_TYPE;
+ scb->hscb->task_management = 0;
+ /*
+ * For Rev A short lun workaround.
+ */
+ scb->hscb->pkt_long_lun[6] = scb->hscb->lun;
+ }
+
+ 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 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]);
+}
+
+#define AHD_COPY_COL_IDX(dst, src) \
+do { \
+ dst->hscb->scsiid = src->hscb->scsiid; \
+ dst->hscb->lun = src->hscb->lun; \
+} while (0)
+
+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
+ * HSCB to download is saved off in ahd->next_queued_hscb.
+ * 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_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_hscb = scb->hscb;
+ scb->hscb = q_hscb;
+
+ /* Now define the mapping from tag to SCB in the scbindex */
+ 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]
+ & QOUTFIFO_ENTRY_VALID_LE) == ahd->qoutfifonext_valid_tag)
+ 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;
+
+ if ((ahd->pause & INTEN) == 0) {
+ /*
+ * Our interrupt is not enabled on the chip
+ * and may be disabled for re-entrancy reasons,
+ * so just return. This is likely just a shared
+ * interrupt.
+ */
+ return;
+ }
+
+ /*
+ * 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.
+ */
+ if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ if (ahd_is_paused(ahd)) {
+ /*
+ * Potentially lost SEQINT.
+ * If SEQINTCODE is non-zero,
+ * simulate the SEQINT.
+ */
+ if (ahd_inb(ahd, SEQINTCODE) != NO_SEQINT)
+ intstat |= SEQINT;
+ }
+ } else {
+ ahd_flush_device_writes(ahd);
+ }
+ ahd_run_qoutfifo(ahd);
+ ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket]++;
+ ahd->cmdcmplt_total++;
+#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/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c
new file mode 100644
index 000000000000..8fb69cd8f90f
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -0,0 +1,5242 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#100 $
+ *
+ * --------------------------------------------------------------------------
+ * 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"
+#include <scsi/scsicam.h>
+
+/*
+ * Include aiclib.c as part of our
+ * "module dependencies are hard" work around.
+ */
+#include "aiclib.c"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+#include <linux/init.h> /* __setup */
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#include "sd.h" /* For geometry detection */
+#endif
+
+#include <linux/mm.h> /* For fetching system memory size */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/unistd.h>
+static int errno;
+
+#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
+
+/*
+ * Bucket size for counting good commands in between bad ones.
+ */
+#define AHD_LINUX_ERR_THRESH 1000
+
+/*
+ * 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
+};
+
+/*
+ * DV option:
+ *
+ * positive value = DV Enabled
+ * zero = DV Disabled
+ * negative value = DV Default for adapter type/seeprom
+ */
+#ifdef CONFIG_AIC79XX_DV_SETTING
+#define AIC79XX_CONFIGED_DV CONFIG_AIC79XX_DV_SETTING
+#else
+#define AIC79XX_CONFIGED_DV -1
+#endif
+
+static int8_t aic79xx_dv_settings[] =
+{
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV
+};
+
+/*
+ * The I/O cell on the chip is very configurable in respect to its analog
+ * characteristics. Set the defaults here; they can be overriden with
+ * the proper insmod parameters.
+ */
+struct ahd_linux_iocell_opts
+{
+ uint8_t precomp;
+ uint8_t slewrate;
+ uint8_t amplitude;
+};
+#define AIC79XX_DEFAULT_PRECOMP 0xFF
+#define AIC79XX_DEFAULT_SLEWRATE 0xFF
+#define AIC79XX_DEFAULT_AMPLITUDE 0xFF
+#define AIC79XX_DEFAULT_IOOPTS \
+{ \
+ AIC79XX_DEFAULT_PRECOMP, \
+ AIC79XX_DEFAULT_SLEWRATE, \
+ AIC79XX_DEFAULT_AMPLITUDE \
+}
+#define AIC79XX_PRECOMP_INDEX 0
+#define AIC79XX_SLEWRATE_INDEX 1
+#define AIC79XX_AMPLITUDE_INDEX 2
+static struct ahd_linux_iocell_opts aic79xx_iocell_info[] =
+{
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS
+};
+
+/*
+ * 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;
+
+/*
+ * There are lots of broken chipsets in the world. Some of them will
+ * violate the PCI spec when we issue byte sized memory writes to our
+ * controller. I/O mapped register access, if allowed by the given
+ * platform, will work in almost all cases.
+ */
+int aic79xx_allow_memio = 1;
+
+/*
+ * 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");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+MODULE_PARM(aic79xx, "s");
+MODULE_PARM_DESC(aic79xx,
+"period delimited, options string.\n"
+" verbose Enable verbose/diagnostic logging\n"
+" allow_memio Allow device registers to be memory mapped\n"
+" debug Bitmask of debug values to enable\n"
+" no_reset Supress initial bus resets\n"
+" extended Enable extended geometry on all controllers\n"
+" periodic_otag Send an ordered tagged transaction\n"
+" periodically to prevent tag starvation.\n"
+" This may be required by some older disk\n"
+" or drives/RAID arrays.\n"
+" reverse_scan Sort PCI devices highest Bus/Slot to lowest\n"
+" tag_info:<tag_str> Set per-target tag depth\n"
+" global_tag_depth:<int> Global tag depth for all targets on all buses\n"
+" rd_strm:<rd_strm_masks> Set per-target read streaming setting.\n"
+" dv:<dv_settings> Set per-controller Domain Validation Setting.\n"
+" slewrate:<slewrate_list>Set the signal slew rate (0-15).\n"
+" precomp:<pcomp_list> Set the signal precompensation (0-7).\n"
+" amplitude:<int> Set the signal amplitude (0-7).\n"
+" seltime:<int> Selection Timeout:\n"
+" (0/256ms,1/128ms,2/64ms,3/32ms)\n"
+"\n"
+" Sample /etc/modules.conf line:\n"
+" Enable verbose logging\n"
+" Set tag depth on Controller 2/Target 2 to 10 tags\n"
+" Shorten the selection timeout to 128ms\n"
+"\n"
+" options aic79xx='\"verbose.tag_info:{{}.{}.{..10}}.seltime:1\"'\n"
+"\n"
+" Sample /etc/modules.conf line:\n"
+" Change Read Streaming for Controller's 2 and 3\n"
+"\n"
+" 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_queue_cmd_complete(struct ahd_softc *ahd,
+ Scsi_Cmnd *cmd);
+static void ahd_linux_filter_inquiry(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+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_thread_run_complete_queue(struct ahd_softc *ahd);
+static void ahd_linux_start_dv(struct ahd_softc *ahd);
+static void ahd_linux_dv_timeout(struct scsi_cmnd *cmd);
+static int ahd_linux_dv_thread(void *data);
+static void ahd_linux_dv_target(struct ahd_softc *ahd, u_int target);
+static void ahd_linux_dv_transition(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static uint32_t aic_error_action(struct scsi_cmnd *cmd,
+ struct scsi_inquiry_data *inq_data);
+static void ahd_linux_dv_fill_cmd(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo);
+static void ahd_linux_dv_inq(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ,
+ u_int request_length);
+static void ahd_linux_dv_tur(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo);
+static void ahd_linux_dv_rebd(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static void ahd_linux_dv_web(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static void ahd_linux_dv_reb(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static void ahd_linux_dv_su(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static __inline int
+ ahd_linux_dv_fallback(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static int ahd_linux_fallback(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static __inline int ahd_linux_dv_fallback(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_linux_dv_complete(Scsi_Cmnd *cmd);
+static void ahd_linux_generate_dv_pattern(struct ahd_linux_target *targ);
+static u_int ahd_linux_user_tagdepth(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static u_int ahd_linux_user_dv_setting(struct ahd_softc *ahd);
+static void ahd_linux_device_queue_depth(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev);
+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, char *s);
+static void ahd_linux_setup_tag_info_global(char *p);
+static void ahd_linux_setup_rd_strm_info(char *p, char *end, char *s);
+static void ahd_linux_setup_dv(char *p, char *end, char *s);
+static void ahd_linux_setup_iocell_info(char *p, char *end, char *s, int index);
+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 int aic79xx_setup(char *c);
+
+/****************************** Inlines ***************************************/
+static __inline void ahd_schedule_completeq(struct ahd_softc *ahd,
+ struct ahd_cmd *acmd);
+static __inline void ahd_schedule_runq(struct ahd_softc *ahd);
+static __inline void ahd_setup_runq_tasklet(struct ahd_softc *ahd);
+static __inline void ahd_teardown_runq_tasklet(struct ahd_softc *ahd);
+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 struct ahd_cmd *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_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 void
+ahd_schedule_completeq(struct ahd_softc *ahd, struct ahd_cmd *acmd)
+{
+ while (acmd != NULL) {
+ struct ahd_completeq *completeq;
+ struct ahd_cmd *list_cmd;
+ struct ahd_cmd *next_cmd;
+
+ next_cmd = TAILQ_NEXT(acmd, acmd_links.tqe);
+ completeq = &ahd->platform_data->completeq;
+ list_cmd = TAILQ_FIRST(completeq);
+ 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);
+ acmd = next_cmd;
+ }
+ if ((ahd->platform_data->flags & AHD_RUN_CMPLT_Q_TIMER) == 0) {
+ ahd->platform_data->flags |= AHD_RUN_CMPLT_Q_TIMER;
+ ahd->platform_data->completeq_timer.expires = jiffies;
+ add_timer(&ahd->platform_data->completeq_timer);
+ }
+}
+
+static __inline void
+ahd_schedule_runq(struct ahd_softc *ahd)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_schedule(&ahd->platform_data->runq_tasklet);
+#else
+ /*
+ * Tasklets are not available, so run inline.
+ */
+ ahd_runq_tasklet((unsigned long)ahd);
+#endif
+}
+
+static __inline
+void ahd_setup_runq_tasklet(struct ahd_softc *ahd)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_init(&ahd->platform_data->runq_tasklet, ahd_runq_tasklet,
+ (unsigned long)ahd);
+#endif
+}
+
+static __inline void
+ahd_teardown_runq_tasklet(struct ahd_softc *ahd)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_kill(&ahd->platform_data->runq_tasklet);
+#endif
+}
+
+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);
+}
+
+#define AHD_LINUX_MAX_RETURNED_ERRORS 4
+static struct ahd_cmd *
+ahd_linux_run_complete_queue(struct ahd_softc *ahd, struct ahd_cmd *acmd)
+{
+ u_long done_flags;
+ int with_errors;
+
+ ahd_done_lock(ahd, &done_flags);
+ with_errors = 0;
+ while (acmd != NULL) {
+ Scsi_Cmnd *cmd;
+
+ cmd = &acmd_scsi_cmd(acmd);
+ acmd = TAILQ_NEXT(acmd, acmd_links.tqe);
+ cmd->host_scribble = NULL;
+ if (ahd_cmd_get_transaction_status(cmd) != DID_OK
+ || (cmd->result & 0xFF) != SCSI_STATUS_OK)
+ with_errors++;
+
+ cmd->scsi_done(cmd);
+
+ if (with_errors > AHD_LINUX_MAX_RETURNED_ERRORS) {
+ /*
+ * Linux uses stack recursion to requeue
+ * commands that need to be retried. Avoid
+ * blowing out the stack by "spoon feeding"
+ * commands that completed with error back
+ * the operating system in case they are going
+ * to be retried. "ick"
+ */
+ break;
+ }
+ }
+ ahd_done_unlock(ahd, &done_flags);
+ return (acmd);
+}
+
+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
+ && AHD_DV_SIMQ_FROZEN(ahd) == 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_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);
+}
+
+/************************ Host template entry points *************************/
+static int ahd_linux_detect(Scsi_Host_Template *);
+static int ahd_linux_release(struct Scsi_Host *);
+static const char *ahd_linux_info(struct Scsi_Host *);
+static int ahd_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int ahd_linux_slave_alloc(Scsi_Device *);
+static int ahd_linux_slave_configure(Scsi_Device *);
+static void ahd_linux_slave_destroy(Scsi_Device *);
+static int ahd_linux_biosparam(struct scsi_device*,
+ struct block_device*, sector_t, int[]);
+#else
+static void ahd_linux_select_queue_depth(struct Scsi_Host *host,
+ Scsi_Device *scsi_devs);
+static int ahd_linux_biosparam(Disk *, kdev_t, int[]);
+#endif
+static int ahd_linux_bus_reset(Scsi_Cmnd *);
+static int ahd_linux_dev_reset(Scsi_Cmnd *);
+static int ahd_linux_abort(Scsi_Cmnd *);
+
+/*
+ * Try to detect an Adaptec 79XX controller.
+ */
+static int
+ahd_linux_detect(Scsi_Host_Template *template)
+{
+ struct ahd_softc *ahd;
+ int found;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+#endif
+
+ /*
+ * 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
+
+ /*
+ * 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.
+ */
+ found = 0;
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+
+ if (ahd_linux_register_host(ahd, template) == 0)
+ found++;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_lock_irq(&io_request_lock);
+#endif
+ aic79xx_detect_complete++;
+ return (found);
+}
+
+/*
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ */
+static 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);
+}
+
+/*
+ * Return a string describing the driver.
+ */
+static 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);
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+static 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_midlayer_entrypoint_lock(ahd, &flags);
+
+ /*
+ * Close the race of a command that was in the process of
+ * being queued to us just as our simq was frozen. Let
+ * DV commands through so long as we are only frozen to
+ * perform DV.
+ */
+ if (ahd->platform_data->qfrozen != 0
+ && AHD_DV_CMD(cmd) == 0) {
+
+ ahd_cmd_set_transaction_status(cmd, CAM_REQUEUE_REQ);
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+ ahd_schedule_completeq(ahd, NULL);
+ ahd_midlayer_entrypoint_unlock(ahd, &flags);
+ return (0);
+ }
+ dev = ahd_linux_get_device(ahd, cmd->channel, cmd->target,
+ cmd->lun, /*alloc*/TRUE);
+ if (dev == NULL) {
+ ahd_midlayer_entrypoint_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_midlayer_entrypoint_unlock(ahd, &flags);
+ return (0);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int
+ahd_linux_slave_alloc(Scsi_Device *device)
+{
+ struct ahd_softc *ahd;
+
+ ahd = *((struct ahd_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Alloc %d\n", ahd_name(ahd), device->id);
+ return (0);
+}
+
+static int
+ahd_linux_slave_configure(Scsi_Device *device)
+{
+ struct ahd_softc *ahd;
+ struct ahd_linux_device *dev;
+ u_long flags;
+
+ ahd = *((struct ahd_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Configure %d\n", ahd_name(ahd), device->id);
+ ahd_midlayer_entrypoint_lock(ahd, &flags);
+ /*
+ * Since Linux has attached to the device, configure
+ * it so we don't free and allocate the device
+ * structure on every command.
+ */
+ dev = ahd_linux_get_device(ahd, device->channel,
+ device->id, device->lun,
+ /*alloc*/TRUE);
+ if (dev != NULL) {
+ dev->flags &= ~AHD_DEV_UNCONFIGURED;
+ dev->flags |= AHD_DEV_SLAVE_CONFIGURED;
+ dev->scsi_device = device;
+ ahd_linux_device_queue_depth(ahd, dev);
+ }
+ ahd_midlayer_entrypoint_unlock(ahd, &flags);
+ return (0);
+}
+
+static void
+ahd_linux_slave_destroy(Scsi_Device *device)
+{
+ struct ahd_softc *ahd;
+ struct ahd_linux_device *dev;
+ u_long flags;
+
+ ahd = *((struct ahd_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Destroy %d\n", ahd_name(ahd), device->id);
+ ahd_midlayer_entrypoint_lock(ahd, &flags);
+ dev = ahd_linux_get_device(ahd, device->channel,
+ device->id, device->lun,
+ /*alloc*/FALSE);
+
+ /*
+ * Filter out "silly" deletions of real devices by only
+ * deleting devices that have had slave_configure()
+ * called on them. All other devices that have not
+ * been configured will automatically be deleted by
+ * the refcounting process.
+ */
+ if (dev != NULL
+ && (dev->flags & AHD_DEV_SLAVE_CONFIGURED) != 0) {
+ dev->flags |= AHD_DEV_UNCONFIGURED;
+ if (TAILQ_EMPTY(&dev->busyq)
+ && dev->active == 0)
+ ahd_linux_free_device(ahd, dev);
+ }
+ ahd_midlayer_entrypoint_unlock(ahd, &flags);
+}
+#else
+/*
+ * 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_midlayer_entrypoint_lock(ahd, &flags);
+ scbnum = 0;
+ for (device = scsi_devs; device != NULL; device = device->next) {
+
+ if (device->host == host) {
+ struct ahd_linux_device *dev;
+
+ /*
+ * Since Linux has attached to the device, configure
+ * it so we don't free and allocate the device
+ * structure on every command.
+ */
+ dev = ahd_linux_get_device(ahd, device->channel,
+ device->id, device->lun,
+ /*alloc*/TRUE);
+ if (dev != NULL) {
+ dev->flags &= ~AHD_DEV_UNCONFIGURED;
+ dev->scsi_device = device;
+ ahd_linux_device_queue_depth(ahd, dev);
+ device->queue_depth = dev->openings
+ + dev->active;
+ if ((dev->flags & (AHD_DEV_Q_BASIC
+ | AHD_DEV_Q_TAGGED)) == 0) {
+ /*
+ * 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;
+ }
+ }
+ }
+ }
+ ahd_midlayer_entrypoint_unlock(ahd, &flags);
+}
+#endif
+
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ahd_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ uint8_t *bh;
+#else
+ahd_linux_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+ struct scsi_device *sdev = disk->device;
+ u_long capacity = disk->capacity;
+ struct buffer_head *bh;
+#endif
+ int heads;
+ int sectors;
+ int cylinders;
+ int ret;
+ int extended;
+ struct ahd_softc *ahd;
+
+ ahd = *((struct ahd_softc **)sdev->host->hostdata);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ bh = scsi_bios_ptable(bdev);
+#elif 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, capacity,
+ &geom[2], &geom[0], &geom[1]);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ kfree(bh);
+#else
+ brelse(bh);
+#endif
+ if (ret != -1)
+ return (ret);
+ }
+ heads = 64;
+ sectors = 32;
+ cylinders = aic_sector_div(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 = aic_sector_div(capacity, heads, sectors);
+ }
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ return (0);
+}
+
+/*
+ * Abort the current SCSI command(s).
+ */
+static 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
+ ahd_midlayer_entrypoint_lock(ahd, &s);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ printf("%s: Abort called for cmd %p\n", ahd_name(ahd), cmd);
+ ahd_dump_card_state(ahd);
+ }
+#endif
+ ahd_midlayer_entrypoint_unlock(ahd, &s);
+ return (FAILED);
+#endif
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+static int
+ahd_linux_dev_reset(Scsi_Cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+#if NOTYET
+ struct ahd_cmd *acmd;
+ u_long s;
+ int found;
+#endif
+
+ ahd = *(struct ahd_softc **)cmd->host->hostdata;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+ printf("%s: Dev reset called for cmd %p\n",
+ ahd_name(ahd), cmd);
+#endif
+#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.
+ */
+static int
+ahd_linux_bus_reset(Scsi_Cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+ struct ahd_cmd *acmd;
+ u_long s;
+ int found;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_unlock_irq(&io_request_lock);
+#endif
+ ahd = *(struct ahd_softc **)cmd->host->hostdata;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+ printf("%s: Bus reset called for cmd %p\n",
+ ahd_name(ahd), cmd);
+#endif
+ ahd_midlayer_entrypoint_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_midlayer_entrypoint_unlock(ahd, &s);
+ if (bootverbose)
+ printf("%s: SCSI bus reset delivered. "
+ "%d SCBs aborted.\n", ahd_name(ahd), found);
+
+ if (acmd != NULL) {
+ acmd = ahd_linux_run_complete_queue(ahd, acmd);
+ if (acmd != NULL) {
+ ahd_midlayer_entrypoint_lock(ahd, &s);
+ ahd_schedule_completeq(ahd, acmd);
+ ahd_midlayer_entrypoint_unlock(ahd, &s);
+ }
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_lock_irq(&io_request_lock);
+#endif
+ return (SUCCESS);
+}
+
+Scsi_Host_Template aic79xx_driver_template = {
+ .proc_info = ahd_linux_proc_info,
+ .detect = ahd_linux_detect,
+ .release = ahd_linux_release,
+ .info = ahd_linux_info,
+ .queuecommand = ahd_linux_queue,
+ .eh_abort_handler = ahd_linux_abort,
+ .eh_device_reset_handler = ahd_linux_dev_reset,
+ .eh_bus_reset_handler = ahd_linux_bus_reset,
+#if defined(__i386__)
+ .bios_param = ahd_linux_biosparam,
+#endif
+ .can_queue = AHD_MAX_QUEUE,
+ .this_id = -1,
+ .sg_tablesize = AHD_NSEG,
+ .cmd_per_lun = 2,
+ .use_clustering = ENABLE_CLUSTERING,
+#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.
+ */
+ .max_sectors = 8192,
+#endif
+#if defined CONFIG_HIGHIO
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+/* Assume RedHat Distribution with its different HIGHIO conventions. */
+ .can_dma_32 = 1,
+ .single_sg_okay = 1,
+#else
+ .highmem_io = 1,
+#endif
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ .name = "aic79xx",
+ .slave_alloc = ahd_linux_slave_alloc,
+ .slave_configure = ahd_linux_slave_configure,
+ .slave_destroy = ahd_linux_slave_destroy,
+#else
+ .select_queue_depths = ahd_linux_select_queue_depth,
+ .use_new_eh_code = 1,
+#endif
+};
+
+#define driver_template aic79xx_driver_template
+#include "scsi_module.c"
+/**************************** 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)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ struct ahd_softc *ahd;
+
+ /*
+ * In 2.5.X, this is called prior to the filesystems
+ * being synced and the SCSI layer being properly
+ * shutdown. A different API is required there,
+ * but the device hooks for this don't quite look
+ * right.
+ */
+ if (event == SYS_DOWN || event == SYS_HALT) {
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+ ahd_shutdown(ahd);
+ }
+ }
+#endif
+ 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 *s)
+{
+ 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 = strsep(&s, ",.");
+}
+
+static void
+ahd_linux_setup_rd_strm_info(char *p, char *end, char *s)
+{
+ char *base;
+ char *tok;
+ char *tok_end;
+ char *tok_end2;
+ int i;
+ int instance;
+ int done;
+ char tok_list[] = {'.', ',', '{', '}', '\0'};
+
+ if (*p != ':')
+ return;
+
+ instance = -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_rd_strm_info))) {
+ aic79xx_rd_strm_info[instance] =
+ simple_strtoul(tok, NULL, 0) & 0xffff;
+ }
+ tok = tok_end;
+ break;
+ }
+ }
+ while ((p != base) && (p != NULL))
+ p = strsep(&s, ",.");
+}
+
+static void
+ahd_linux_setup_dv(char *p, char *end, char *s)
+{
+ char *base;
+ char *tok;
+ char *tok_end;
+ char *tok_end2;
+ int i;
+ int instance;
+ int done;
+ char tok_list[] = {'.', ',', '{', '}', '\0'};
+
+ if (*p != ':')
+ return;
+
+ instance = -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_dv_settings))
+ 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_dv_settings))) {
+ aic79xx_dv_settings[instance] =
+ simple_strtol(tok, NULL, 0);
+ }
+ tok = tok_end;
+ break;
+ }
+ }
+ while ((p != base) && (p != NULL))
+ p = strsep(&s, ",.");
+}
+
+static void
+ahd_linux_setup_iocell_info(char *p, char *end, char *s, int index)
+{
+ char *base;
+ char *tok;
+ char *tok_end;
+ char *tok_end2;
+ uint8_t *iocell_info;
+ int i;
+ int instance;
+ int done;
+ char tok_list[] = {'.', ',', '{', '}', '\0'};
+
+ if (*p != ':')
+ return;
+
+ instance = -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_iocell_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_iocell_info))) {
+ iocell_info =
+ (uint8_t*)&aic79xx_iocell_info[instance];
+ iocell_info[index] =
+ simple_strtoul(tok, NULL, 0) & 0xffff;
+ }
+ tok = tok_end;
+ break;
+ }
+ }
+ while ((p != base) && (p != NULL))
+ p = strsep(&s, ",.");
+}
+
+static void
+ahd_linux_setup_tag_info_global(char *p)
+{
+ int tags, i, j;
+
+ tags = simple_strtoul(p + 1, NULL, 0) & 0xff;
+ printf("Setting Global Tags= %d\n", tags);
+
+ for (i = 0; i < NUM_ELEMENTS(aic79xx_tag_info); i++) {
+ for (j = 0; j < AHD_NUM_TARGETS; j++) {
+ aic79xx_tag_info[i].tag_commands[j] = tags;
+ }
+ }
+}
+
+/*
+ * 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
+ */
+static 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 },
+ { "allow_memio", &aic79xx_allow_memio},
+#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 },
+ { "global_tag_depth", NULL},
+ { "rd_strm", NULL },
+ { "dv", NULL },
+ { "slewrate", NULL },
+ { "precomp", NULL },
+ { "amplitude", NULL },
+ };
+
+ end = strchr(s, '\0');
+
+ while ((p = strsep(&s, ",.")) != NULL) {
+ if (*p == '\0')
+ continue;
+ 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, "global_tag_depth", n)) {
+ ahd_linux_setup_tag_info_global(p + n);
+ } else if (!strncmp(p, "tag_info", n)) {
+ ahd_linux_setup_tag_info(p + n, end, s);
+ } else if (strncmp(p, "rd_strm", n) == 0) {
+ ahd_linux_setup_rd_strm_info(p + n, end, s);
+ } else if (strncmp(p, "dv", n) == 0) {
+ ahd_linux_setup_dv(p + n, end, s);
+ } else if (strncmp(p, "slewrate", n) == 0) {
+ ahd_linux_setup_iocell_info(p + n, end, s,
+ AIC79XX_SLEWRATE_INDEX);
+ } else if (strncmp(p, "precomp", n) == 0) {
+ ahd_linux_setup_iocell_info(p + n, end, s,
+ AIC79XX_PRECOMP_INDEX);
+ } else if (strncmp(p, "amplitude", n) == 0) {
+ ahd_linux_setup_iocell_info(p + n, end, s,
+ AIC79XX_AMPLITUDE_INDEX);
+ } 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;
+
+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;
+ u_long target;
+
+ template->name = ahd->description;
+ host = scsi_register(template, sizeof(struct ahd_softc *));
+ if (host == NULL)
+ return (ENOMEM);
+
+ *((struct ahd_softc **)host->hostdata) = ahd;
+ ahd_lock(ahd, &s);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ scsi_assign_lock(host, &ahd->platform_data->spin_lock);
+#endif
+ ahd->platform_data->host = host;
+ host->can_queue = AHD_MAX_QUEUE;
+ host->cmd_per_lun = 2;
+ host->sg_tablesize = AHD_NSEG;
+ 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);
+ ahd->platform_data->dv_pid = kernel_thread(ahd_linux_dv_thread, ahd, 0);
+ ahd_lock(ahd, &s);
+ if (ahd->platform_data->dv_pid < 0) {
+ printf("%s: Failed to create DV thread, error= %d\n",
+ ahd_name(ahd), ahd->platform_data->dv_pid);
+ return (-ahd->platform_data->dv_pid);
+ }
+ /*
+ * Initially allocate *all* of our linux target objects
+ * so that the DV thread will scan them all in parallel
+ * just after driver initialization. Any device that
+ * does not exist will have its target object destroyed
+ * by the selection timeout handler. In the case of a
+ * device that appears after the initial DV scan, async
+ * negotiation will occur for the first command, and DV
+ * will comence should that first command be successful.
+ */
+ for (target = 0; target < AHD_NUM_TARGETS; target++)
+ ahd_linux_alloc_target(ahd, 0, target);
+ ahd_intr_enable(ahd, TRUE);
+ ahd_linux_start_dv(ahd);
+ ahd_unlock(ahd, &s);
+ return (0);
+}
+
+uint64_t
+ahd_linux_get_memsize()
+{
+ struct sysinfo si;
+
+ si_meminfo(&si);
+ return ((uint64_t)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.
+ */
+static 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;
+
+ /*
+ * Force negotiation to async for all targets that
+ * will not see an initial bus reset.
+ */
+ 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);
+ ahd_compile_devinfo(&devinfo, our_id, target_id,
+ CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);
+ ahd_update_neg_request(ahd, &devinfo, tstate,
+ tinfo, AHD_NEG_ALWAYS);
+ }
+ /* 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);
+ init_timer(&ahd->platform_data->completeq_timer);
+ ahd->platform_data->completeq_timer.data = (u_long)ahd;
+ ahd->platform_data->completeq_timer.function =
+ (ahd_linux_callback_t *)ahd_linux_thread_run_complete_queue;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ init_MUTEX_LOCKED(&ahd->platform_data->eh_sem);
+ init_MUTEX_LOCKED(&ahd->platform_data->dv_sem);
+ init_MUTEX_LOCKED(&ahd->platform_data->dv_cmd_sem);
+#else
+ ahd->platform_data->eh_sem = MUTEX_LOCKED;
+ ahd->platform_data->dv_sem = MUTEX_LOCKED;
+ ahd->platform_data->dv_cmd_sem = MUTEX_LOCKED;
+#endif
+ ahd_setup_runq_tasklet(ahd);
+ 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)
+{
+ u_long s;
+
+ if (ahd->platform_data != NULL) {
+ /* Kill the DV kthread */
+ if (ahd->platform_data->dv_pid != 0) {
+ ahd_lock(ahd, &s);
+ ahd->platform_data->flags |= AHD_DV_SHUTDOWN;
+ ahd_unlock(ahd, &s);
+ up(&ahd->platform_data->dv_sem);
+ do {
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ printf("%s: Waiting for DV thread to "
+ "exit\n", ahd_name(ahd));
+ }
+#endif
+ } while (waitpid(ahd->platform_data->dv_pid, NULL,
+ __WCLONE) == -ERESTARTSYS);
+ }
+ ahd_teardown_runq_tasklet(ahd);
+ 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_init(struct ahd_softc *ahd)
+{
+ /*
+ * Lookup and commit any modified IO Cell options.
+ */
+ if (ahd->unit < NUM_ELEMENTS(aic79xx_iocell_info)) {
+ struct ahd_linux_iocell_opts *iocell_opts;
+
+ iocell_opts = &aic79xx_iocell_info[ahd->unit];
+ if (iocell_opts->precomp != AIC79XX_DEFAULT_PRECOMP)
+ AHD_SET_PRECOMP(ahd, iocell_opts->precomp);
+ if (iocell_opts->slewrate != AIC79XX_DEFAULT_SLEWRATE)
+ AHD_SET_SLEWRATE(ahd, iocell_opts->slewrate);
+ if (iocell_opts->amplitude != AIC79XX_DEFAULT_AMPLITUDE)
+ AHD_SET_AMPLITUDE(ahd, iocell_opts->amplitude);
+ }
+
+}
+
+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 (dev->maxtags == 0) {
+ /*
+ * Queueing is disabled by the user.
+ */
+ dev->openings = 1;
+ } else 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;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ if (dev->scsi_device != NULL) {
+ switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) {
+ case AHD_DEV_Q_BASIC:
+ scsi_adjust_queue_depth(dev->scsi_device,
+ MSG_SIMPLE_TASK,
+ dev->openings + dev->active);
+ break;
+ case AHD_DEV_Q_TAGGED:
+ scsi_adjust_queue_depth(dev->scsi_device,
+ MSG_ORDERED_TASK,
+ dev->openings + dev->active);
+ break;
+ default:
+ /*
+ * 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.
+ */
+ scsi_adjust_queue_depth(dev->scsi_device,
+ /*NON-TAGGED*/0,
+ /*queue depth*/2);
+ break;
+ }
+ }
+#endif
+}
+
+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);
+}
+
+static void
+ahd_linux_thread_run_complete_queue(struct ahd_softc *ahd)
+{
+ struct ahd_cmd *acmd;
+ u_long flags;
+
+ ahd_lock(ahd, &flags);
+ del_timer(&ahd->platform_data->completeq_timer);
+ ahd->platform_data->flags &= ~AHD_RUN_CMPLT_Q_TIMER;
+ acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+ TAILQ_INIT(&ahd->platform_data->completeq);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &flags);
+#endif
+ if (acmd != NULL) {
+ acmd = ahd_linux_run_complete_queue(ahd, acmd);
+ if (acmd != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_lock(ahd, &flags);
+#endif
+ ahd_schedule_completeq(ahd, acmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &flags);
+#endif
+ }
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &flags);
+#endif
+}
+
+static void
+ahd_linux_start_dv(struct ahd_softc *ahd)
+{
+
+ /*
+ * Freeze the simq and signal ahd_linux_queue to not let any
+ * more commands through
+ */
+ if ((ahd->platform_data->flags & AHD_DV_ACTIVE) == 0) {
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("%s: Starting DV\n", ahd_name(ahd));
+#endif
+
+ ahd->platform_data->flags |= AHD_DV_ACTIVE;
+ ahd_freeze_simq(ahd);
+
+ /* Wake up the DV kthread */
+ up(&ahd->platform_data->dv_sem);
+ }
+}
+
+static int
+ahd_linux_dv_thread(void *data)
+{
+ struct ahd_softc *ahd;
+ int target;
+ u_long s;
+
+ ahd = (struct ahd_softc *)data;
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("In DV Thread\n");
+#endif
+
+ while (1) {
+ down(&ahd->platform_data->dv_sem);
+
+ /* Check to see if we've been signaled to exit */
+ ahd_lock(ahd, &s);
+ if ((ahd->platform_data->flags & AHD_DV_SHUTDOWN) != 0) {
+ ahd_unlock(ahd, &s);
+ return (0);
+ }
+ ahd_unlock(ahd, &s);
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("%s: Beginning Domain Validation\n",
+ ahd_name(ahd));
+#endif
+
+ /*
+ * Wait for any pending commands to drain before proceeding.
+ */
+ ahd_lock(ahd, &s);
+ while (LIST_FIRST(&ahd->pending_scbs) != NULL) {
+ ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_EMPTY;
+ ahd_unlock(ahd, &s);
+ down(&ahd->platform_data->dv_sem);
+ ahd_lock(ahd, &s);
+ }
+
+ /*
+ * Wait for the SIMQ to be released so that DV is the
+ * only reason the queue is frozen.
+ */
+ while (AHD_DV_SIMQ_FROZEN(ahd) == 0) {
+ ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_RELEASE;
+ ahd_unlock(ahd, &s);
+ down(&ahd->platform_data->dv_sem);
+ ahd_lock(ahd, &s);
+ }
+ ahd_unlock(ahd, &s);
+
+ for (target = 0; target < AHD_NUM_TARGETS; target++)
+ ahd_linux_dv_target(ahd, target);
+
+ ahd_lock(ahd, &s);
+ ahd->platform_data->flags &= ~AHD_DV_ACTIVE;
+ ahd_unlock(ahd, &s);
+
+ /*
+ * Release the SIMQ so that normal commands are
+ * allowed to continue on the bus.
+ */
+ ahd_release_simq(ahd);
+ }
+
+ return (0);
+}
+
+#define AHD_LINUX_DV_INQ_SHORT_LEN 36
+#define AHD_LINUX_DV_INQ_LEN 256
+#define AHD_LINUX_DV_TIMEOUT (HZ / 4)
+
+#define AHD_SET_DV_STATE(ahd, targ, newstate) \
+ ahd_set_dv_state(ahd, targ, newstate, __LINE__)
+
+static __inline void
+ahd_set_dv_state(struct ahd_softc *ahd, struct ahd_linux_target *targ,
+ ahd_dv_state newstate, u_int line)
+{
+ ahd_dv_state oldstate;
+
+ oldstate = targ->dv_state;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("%s:%d: Going from state %d to state %d\n",
+ ahd_name(ahd), line, oldstate, newstate);
+#endif
+
+ if (oldstate == newstate)
+ targ->dv_state_retry++;
+ else
+ targ->dv_state_retry = 0;
+ targ->dv_state = newstate;
+}
+
+static void
+ahd_linux_dv_target(struct ahd_softc *ahd, u_int target_offset)
+{
+ struct ahd_devinfo devinfo;
+ struct ahd_linux_target *targ;
+ struct scsi_cmnd *cmd;
+ struct scsi_sense_data *sense;
+ uint8_t *buffer;
+ u_long s;
+ u_int timeout;
+ int echo_size;
+
+ sense = NULL;
+ buffer = NULL;
+ echo_size = 0;
+ ahd_lock(ahd, &s);
+ targ = ahd->platform_data->targets[target_offset];
+ if (targ == NULL || (targ->flags & AHD_DV_REQUIRED) == 0) {
+ ahd_unlock(ahd, &s);
+ return;
+ }
+ ahd_compile_devinfo(&devinfo, ahd->our_id, targ->target, /*lun*/0,
+ targ->channel + 'A', ROLE_INITIATOR);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Performing DV\n");
+ }
+#endif
+
+ ahd_unlock(ahd, &s);
+
+ cmd = malloc(sizeof(struct scsi_cmnd), M_DEVBUF, M_WAITOK);
+
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_INQ_SHORT_ASYNC);
+
+ while (targ->dv_state != AHD_DV_STATE_EXIT) {
+ timeout = AHD_LINUX_DV_TIMEOUT;
+ switch (targ->dv_state) {
+ case AHD_DV_STATE_INQ_SHORT_ASYNC:
+ case AHD_DV_STATE_INQ_ASYNC:
+ case AHD_DV_STATE_INQ_ASYNC_VERIFY:
+ /*
+ * Set things to async narrow to reduce the
+ * chance that the INQ will fail.
+ */
+ ahd_lock(ahd, &s);
+ ahd_set_syncrate(ahd, &devinfo, 0, 0, 0,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_unlock(ahd, &s);
+ timeout = 10 * HZ;
+ /* FALLTHROUGH */
+ case AHD_DV_STATE_INQ_VERIFY:
+ {
+ u_int inq_len;
+
+ if (targ->dv_state == AHD_DV_STATE_INQ_SHORT_ASYNC)
+ inq_len = AHD_LINUX_DV_INQ_SHORT_LEN;
+ else
+ inq_len = targ->inq_data->additional_length + 5;
+ ahd_linux_dv_inq(ahd, cmd, &devinfo, targ, inq_len);
+ break;
+ }
+ case AHD_DV_STATE_TUR:
+ case AHD_DV_STATE_BUSY:
+ ahd_linux_dv_tur(ahd, cmd, &devinfo);
+ break;
+ case AHD_DV_STATE_REBD:
+ ahd_linux_dv_rebd(ahd, cmd, &devinfo, targ);
+ break;
+ case AHD_DV_STATE_WEB:
+ ahd_linux_dv_web(ahd, cmd, &devinfo, targ);
+ break;
+
+ case AHD_DV_STATE_REB:
+ ahd_linux_dv_reb(ahd, cmd, &devinfo, targ);
+ break;
+
+ case AHD_DV_STATE_SU:
+ ahd_linux_dv_su(ahd, cmd, &devinfo, targ);
+ timeout = 50 * HZ;
+ break;
+
+ default:
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Unknown DV state %d\n", targ->dv_state);
+ goto out;
+ }
+
+ /* Queue the command and wait for it to complete */
+ /* Abuse eh_timeout in the scsi_cmnd struct for our purposes */
+ init_timer(&cmd->eh_timeout);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ /*
+ * All of the printfs during negotiation
+ * really slow down the negotiation.
+ * Add a bit of time just to be safe.
+ */
+ timeout += HZ;
+#endif
+ scsi_add_timer(cmd, timeout, ahd_linux_dv_timeout);
+ /*
+ * In 2.5.X, it is assumed that all calls from the
+ * "midlayer" (which we are emulating) will have the
+ * ahd host lock held.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahd_lock(ahd, &s);
+#endif
+ ahd_linux_queue(cmd, ahd_linux_dv_complete);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &s);
+#endif
+ down(&ahd->platform_data->dv_cmd_sem);
+ /*
+ * Wait for the SIMQ to be released so that DV is the
+ * only reason the queue is frozen.
+ */
+ ahd_lock(ahd, &s);
+ while (AHD_DV_SIMQ_FROZEN(ahd) == 0) {
+ ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_RELEASE;
+ ahd_unlock(ahd, &s);
+ down(&ahd->platform_data->dv_sem);
+ ahd_lock(ahd, &s);
+ }
+ ahd_unlock(ahd, &s);
+
+ ahd_linux_dv_transition(ahd, cmd, &devinfo, targ);
+ }
+
+out:
+ if (cmd != NULL)
+ free(cmd, M_DEVBUF);
+
+ ahd_lock(ahd, &s);
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->flags &= ~AHD_DV_REQUIRED;
+ if (targ->refcount == 0)
+ ahd_linux_free_target(ahd, targ);
+ ahd_unlock(ahd, &s);
+}
+
+static void
+ahd_linux_dv_transition(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ)
+{
+ u_int32_t status;
+
+ status = aic_error_action(cmd, targ->inq_data);
+
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Entering ahd_linux_dv_transition, state= %d, "
+ "status= 0x%x, cmd->result= 0x%x\n", targ->dv_state,
+ status, cmd->result);
+ }
+#endif
+
+ switch (targ->dv_state) {
+ case AHD_DV_STATE_INQ_SHORT_ASYNC:
+ case AHD_DV_STATE_INQ_ASYNC:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state+1);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+ if ((status & SS_ERRMASK) == EBUSY)
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+ case AHD_DV_STATE_INQ_ASYNC_VERIFY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ u_int spi3data;
+
+ if (memcmp(targ->inq_data, targ->dv_buffer,
+ AHD_LINUX_DV_INQ_LEN) != 0) {
+ /*
+ * Inquiry data must have changed.
+ * Try from the top again.
+ */
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ }
+
+ if (ahd_linux_user_dv_setting(ahd) == 0) {
+ ahd_linux_filter_inquiry(ahd, devinfo);
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+
+ spi3data = targ->inq_data->spi3data;
+ switch (spi3data & SID_SPI_CLOCK_DT_ST) {
+ default:
+ case SID_SPI_CLOCK_ST:
+ /* Assume only basic DV is supported. */
+ ahd_linux_filter_inquiry(ahd, devinfo);
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_VERIFY);
+ break;
+ case SID_SPI_CLOCK_DT:
+ case SID_SPI_CLOCK_DT_ST:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_REBD);
+ break;
+ }
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+
+ if ((status & SS_ERRMASK) == EBUSY)
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+ case AHD_DV_STATE_INQ_VERIFY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+
+ if (memcmp(targ->inq_data, targ->dv_buffer,
+ AHD_LINUX_DV_INQ_LEN) == 0) {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ } else if ((status & SS_ERRMASK) == EBUSY)
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_TUR:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_ASYNC);
+ break;
+ case SS_RETRY:
+ case SS_TUR:
+ if ((status & SS_ERRMASK) == EBUSY) {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
+ break;
+ }
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ }
+ if (targ->dv_state_retry >= 10) {
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV TUR reties exhausted\n");
+ }
+#endif
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ if (status & SSQ_DELAY)
+ scsi_sleep(1 * HZ);
+
+ break;
+ case SS_START:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_SU);
+ break;
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_REBD:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ uint32_t echo_size;
+
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_WEB);
+ echo_size = scsi_3btoul(&targ->dv_buffer[1]);
+ echo_size &= 0x1FFF;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Echo buffer size= %d\n", echo_size);
+ }
+#endif
+ if (echo_size == 0) {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+
+ /* Generate the buffer pattern */
+ targ->dv_echo_size = echo_size;
+ ahd_linux_generate_dv_pattern(targ);
+ /*
+ * Setup initial negotiation values.
+ */
+ ahd_linux_filter_inquiry(ahd, devinfo);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+ if (targ->dv_state_retry <= 10)
+ break;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV REBD reties exhausted\n");
+ }
+#endif
+ /* FALLTHROUGH */
+ case SS_FATAL:
+ default:
+ /*
+ * Setup initial negotiation values
+ * and try level 1 DV.
+ */
+ ahd_linux_filter_inquiry(ahd, devinfo);
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_INQ_VERIFY);
+ targ->dv_echo_size = 0;
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_WEB:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_REB);
+ break;
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ }
+ if (targ->dv_state_retry <= 10)
+ break;
+ /* FALLTHROUGH */
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV WEB reties exhausted\n");
+ }
+#endif
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_REB:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ if (memcmp(targ->dv_buffer, targ->dv_buffer1,
+ targ->dv_echo_size) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0)
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ else
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_WEB);
+ break;
+ }
+
+ if (targ->dv_buffer != NULL) {
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = NULL;
+ }
+ if (targ->dv_buffer1 != NULL) {
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = NULL;
+ }
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ break;
+ }
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_WEB);
+ }
+ if (targ->dv_state_retry <= 10) {
+ if ((status & (SSQ_DELAY_RANDOM|SSQ_DELAY))!= 0)
+ scsi_sleep(ahd->our_id*HZ/10);
+ break;
+ }
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV REB reties exhausted\n");
+ }
+#endif
+ /* FALLTHROUGH */
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_SU:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_BUSY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if (targ->dv_state_retry < 60) {
+ if ((status & SSQ_DELAY) != 0)
+ scsi_sleep(1 * HZ);
+ } else {
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV BUSY reties exhausted\n");
+ }
+#endif
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ }
+ break;
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ default:
+ printf("%s: Invalid DV completion state %d\n", ahd_name(ahd),
+ targ->dv_state);
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+}
+
+static uint32_t
+aic_error_action(struct scsi_cmnd *cmd, struct scsi_inquiry_data *inq_data)
+{
+ aic_sense_action err_action;
+ cam_status status;
+ u_int scsi_status;
+ int sense;
+
+ status = ahd_cmd_get_transaction_status(cmd);
+ scsi_status = ahd_cmd_get_scsi_status(cmd);
+ sense = (cmd->result >> 24) == DRIVER_SENSE;
+
+ switch (status) {
+ case CAM_REQ_CMP:
+ err_action = SS_NOP;
+ break;
+ case CAM_AUTOSENSE_FAIL:
+ case CAM_SCSI_STATUS_ERROR:
+
+ switch (scsi_status) {
+ case SCSI_STATUS_OK:
+ case SCSI_STATUS_COND_MET:
+ case SCSI_STATUS_INTERMED:
+ case SCSI_STATUS_INTERMED_COND_MET:
+ err_action = SS_NOP;
+ break;
+ case SCSI_STATUS_CMD_TERMINATED:
+ case SCSI_STATUS_CHECK_COND:
+ if (sense != 0) {
+ struct scsi_sense_data *sense;
+
+ sense = (struct scsi_sense_data *)
+ &cmd->sense_buffer;
+ err_action =
+ aic_sense_error_action(sense, inq_data, 0);
+
+ } else {
+ err_action = SS_RETRY|SSQ_FALLBACK
+ | SSQ_DECREMENT_COUNT|EIO;
+ }
+ break;
+ case SCSI_STATUS_QUEUE_FULL:
+ case SCSI_STATUS_BUSY:
+ err_action = SS_RETRY|SSQ_DELAY|SSQ_MANY
+ | SSQ_DECREMENT_COUNT|EBUSY;
+ break;
+ case SCSI_STATUS_RESERV_CONFLICT:
+ default:
+ err_action = SS_FAIL|EBUSY;
+ break;
+ }
+ break;
+ case CAM_CMD_TIMEOUT:
+ case CAM_REQ_CMP_ERR:
+ case CAM_UNEXP_BUSFREE:
+ case CAM_UNCOR_PARITY:
+ case CAM_DATA_RUN_ERR:
+ err_action = SS_RETRY|SSQ_FALLBACK|EIO;
+ break;
+ case CAM_UA_ABORT:
+ case CAM_UA_TERMIO:
+ case CAM_MSG_REJECT_REC:
+ case CAM_SEL_TIMEOUT:
+ err_action = SS_FAIL|EIO;
+ break;
+ case CAM_REQ_INVALID:
+ case CAM_PATH_INVALID:
+ case CAM_DEV_NOT_THERE:
+ case CAM_NO_HBA:
+ case CAM_PROVIDE_FAIL:
+ case CAM_REQ_TOO_BIG:
+ case CAM_RESRC_UNAVAIL:
+ case CAM_BUSY:
+ default:
+ /* panic?? These should never occur in our application. */
+ err_action = SS_FAIL|EIO;
+ break;
+ case CAM_SCSI_BUS_RESET:
+ case CAM_BDR_SENT:
+ case CAM_REQUEUE_REQ:
+ /* Unconditional requeue */
+ err_action = SS_RETRY;
+ break;
+ }
+
+ return (err_action);
+}
+
+static void
+ahd_linux_dv_fill_cmd(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo)
+{
+ memset(cmd, 0, sizeof(struct scsi_cmnd));
+ cmd->host = ahd->platform_data->host;
+ cmd->scsi_done = ahd_linux_dv_complete;
+ cmd->target = devinfo->target;
+ cmd->lun = devinfo->lun;
+ cmd->channel = devinfo->channel - 'A';
+}
+
+/*
+ * Synthesize an inquiry command. On the return trip, it'll be
+ * sniffed and the device transfer settings set for us.
+ */
+static void
+ahd_linux_dv_inq(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo, struct ahd_linux_target *targ,
+ u_int request_length)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending INQ\n");
+ }
+#endif
+ if (targ->inq_data == NULL)
+ targ->inq_data = malloc(AHD_LINUX_DV_INQ_LEN,
+ M_DEVBUF, M_WAITOK);
+ if (targ->dv_state > AHD_DV_STATE_INQ_ASYNC) {
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(AHD_LINUX_DV_INQ_LEN,
+ M_DEVBUF, M_WAITOK);
+ }
+
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = INQUIRY;
+ cmd->cmnd[4] = request_length;
+ cmd->request_bufflen = request_length;
+ if (targ->dv_state > AHD_DV_STATE_INQ_ASYNC)
+ cmd->request_buffer = targ->dv_buffer;
+ else
+ cmd->request_buffer = targ->inq_data;
+ memset(cmd->request_buffer, 0, AHD_LINUX_DV_INQ_LEN);
+}
+
+static void
+ahd_linux_dv_tur(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending TUR\n");
+ }
+#endif
+ /* Do a TUR to clear out any non-fatal transitional state */
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_NONE;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = TEST_UNIT_READY;
+}
+
+#define AHD_REBD_LEN 4
+
+static void
+ahd_linux_dv_rebd(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo, struct ahd_linux_target *targ)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending REBD\n");
+ }
+#endif
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(AHD_REBD_LEN, M_DEVBUF, M_WAITOK);
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = READ_BUFFER;
+ cmd->cmnd[1] = 0x0b;
+ scsi_ulto3b(AHD_REBD_LEN, &cmd->cmnd[6]);
+ cmd->request_bufflen = AHD_REBD_LEN;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer;
+}
+
+static void
+ahd_linux_dv_web(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo, struct ahd_linux_target *targ)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending WEB\n");
+ }
+#endif
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_WRITE;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = WRITE_BUFFER;
+ cmd->cmnd[1] = 0x0a;
+ scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]);
+ cmd->request_bufflen = targ->dv_echo_size;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer;
+}
+
+static void
+ahd_linux_dv_reb(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo, struct ahd_linux_target *targ)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending REB\n");
+ }
+#endif
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = READ_BUFFER;
+ cmd->cmnd[1] = 0x0a;
+ scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]);
+ cmd->request_bufflen = targ->dv_echo_size;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer1;
+}
+
+static void
+ahd_linux_dv_su(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ)
+{
+ u_int le;
+
+ le = SID_IS_REMOVABLE(targ->inq_data) ? SSS_LOEJ : 0;
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending SU\n");
+ }
+#endif
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_NONE;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = START_STOP_UNIT;
+ cmd->cmnd[4] = le | SSS_START;
+}
+
+/*
+ * Return speed in KB/s.
+ */
+static u_int
+ahd_linux_calc_speed(u_int width, u_int period, u_int offset)
+{
+ u_int freq;
+
+ if (offset != 0 && period < AHD_SYNCRATE_MIN)
+ freq = aic_calc_syncsrate(period);
+ else
+ /* Roughly 3.3MB/s for async */
+ freq = 3300;
+ freq <<= width;
+ return (freq);
+}
+
+static __inline int
+ahd_linux_dv_fallback(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ u_long s;
+ int retval;
+
+ ahd_lock(ahd, &s);
+ retval = ahd_linux_fallback(ahd, devinfo);
+ ahd_unlock(ahd, &s);
+
+ return (retval);
+}
+
+static int
+ahd_linux_fallback(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ struct ahd_linux_target *targ;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_transinfo *goal;
+ struct ahd_tmode_tstate *tstate;
+ u_int width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+ u_int cur_speed;
+ u_int wide_speed;
+ u_int narrow_speed;
+ u_int fallback_speed;
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Trying to fallback\n");
+ }
+#endif
+ targ = ahd->platform_data->targets[devinfo->target_offset];
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ goal = &tinfo->goal;
+ width = goal->width;
+ period = goal->period;
+ offset = goal->offset;
+ ppr_options = goal->ppr_options;
+ if (offset == 0)
+ period = AHD_ASYNC_XFER_PERIOD;
+ if (targ->dv_next_narrow_period == 0)
+ targ->dv_next_narrow_period = MAX(period, AHD_SYNCRATE_ULTRA2);
+ if (targ->dv_next_wide_period == 0)
+ targ->dv_next_wide_period = period;
+ if (targ->dv_max_ppr_options == 0)
+ targ->dv_max_ppr_options = ppr_options;
+ if (targ->dv_last_ppr_options == 0)
+ targ->dv_last_ppr_options = ppr_options;
+
+ cur_speed = ahd_linux_calc_speed(width, period, offset);
+ wide_speed = ahd_linux_calc_speed(MSG_EXT_WDTR_BUS_16_BIT,
+ targ->dv_next_wide_period,
+ MAX_OFFSET);
+ narrow_speed = ahd_linux_calc_speed(MSG_EXT_WDTR_BUS_8_BIT,
+ targ->dv_next_narrow_period,
+ MAX_OFFSET);
+ fallback_speed = ahd_linux_calc_speed(width, period+1, offset);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ printf("cur_speed= %d, wide_speed= %d, narrow_speed= %d, "
+ "fallback_speed= %d\n", cur_speed, wide_speed,
+ narrow_speed, fallback_speed);
+ }
+#endif
+
+ if (cur_speed > 160000) {
+ /*
+ * Paced/DT/IU_REQ only transfer speeds. All we
+ * can do is fallback in terms of syncrate.
+ */
+ period++;
+ } else if (cur_speed > 80000) {
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ /*
+ * Try without IU_REQ as it may be confusing
+ * an expander.
+ */
+ ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+ } else {
+ /*
+ * Paced/DT only transfer speeds. All we
+ * can do is fallback in terms of syncrate.
+ */
+ period++;
+ ppr_options = targ->dv_max_ppr_options;
+ }
+ } else if (cur_speed > 3300) {
+
+ /*
+ * In this range we the following
+ * options ordered from highest to
+ * lowest desireability:
+ *
+ * o Wide/DT
+ * o Wide/non-DT
+ * o Narrow at a potentally higher sync rate.
+ *
+ * All modes are tested with and without IU_REQ
+ * set since using IUs may confuse an expander.
+ */
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+
+ ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+ } else if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+ /*
+ * Try going non-DT.
+ */
+ ppr_options = targ->dv_max_ppr_options;
+ ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ } else if (targ->dv_last_ppr_options != 0) {
+ /*
+ * Try without QAS or any other PPR options.
+ * We may need a non-PPR message to work with
+ * an expander. We look at the "last PPR options"
+ * so we will perform this fallback even if the
+ * target responded to our PPR negotiation with
+ * no option bits set.
+ */
+ ppr_options = 0;
+ } else if (width == MSG_EXT_WDTR_BUS_16_BIT) {
+ /*
+ * If the next narrow speed is greater than
+ * the next wide speed, fallback to narrow.
+ * Otherwise fallback to the next DT/Wide setting.
+ * The narrow async speed will always be smaller
+ * than the wide async speed, so handle this case
+ * specifically.
+ */
+ ppr_options = targ->dv_max_ppr_options;
+ if (narrow_speed > fallback_speed
+ || period >= AHD_ASYNC_XFER_PERIOD) {
+ targ->dv_next_wide_period = period+1;
+ width = MSG_EXT_WDTR_BUS_8_BIT;
+ period = targ->dv_next_narrow_period;
+ } else {
+ period++;
+ }
+ } else if ((ahd->features & AHD_WIDE) != 0
+ && tinfo->user.width != 0
+ && wide_speed >= fallback_speed
+ && (targ->dv_next_wide_period <= AHD_ASYNC_XFER_PERIOD
+ || period >= AHD_ASYNC_XFER_PERIOD)) {
+
+ /*
+ * We are narrow. Try falling back
+ * to the next wide speed with
+ * all supported ppr options set.
+ */
+ targ->dv_next_narrow_period = period+1;
+ width = MSG_EXT_WDTR_BUS_16_BIT;
+ period = targ->dv_next_wide_period;
+ ppr_options = targ->dv_max_ppr_options;
+ } else {
+ /* Only narrow fallback is allowed. */
+ period++;
+ ppr_options = targ->dv_max_ppr_options;
+ }
+ } else {
+ return (-1);
+ }
+ offset = MAX_OFFSET;
+ ahd_find_syncrate(ahd, &period, &ppr_options, AHD_SYNCRATE_PACED);
+ ahd_set_width(ahd, devinfo, width, AHD_TRANS_GOAL, FALSE);
+ if (period == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ if (width == MSG_EXT_WDTR_BUS_8_BIT)
+ targ->dv_next_narrow_period = AHD_ASYNC_XFER_PERIOD;
+ else
+ targ->dv_next_wide_period = AHD_ASYNC_XFER_PERIOD;
+ }
+ ahd_set_syncrate(ahd, devinfo, period, offset,
+ ppr_options, AHD_TRANS_GOAL, FALSE);
+ targ->dv_last_ppr_options = ppr_options;
+ return (0);
+}
+
+static void
+ahd_linux_dv_timeout(struct scsi_cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+ struct ahd_cmd *acmd;
+ struct ahd_linux_device *next_dev;
+ struct scb *scb;
+ u_long flags;
+
+ ahd = *((struct ahd_softc **)cmd->host->hostdata);
+ ahd_lock(ahd, &flags);
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("%s: Timeout while doing DV command %x.\n",
+ ahd_name(ahd), cmd->cmnd[0]);
+#endif
+
+ /*
+ * Guard against "done race". No action is
+ * required if we just completed.
+ */
+ if ((scb = (struct scb *)cmd->host_scribble) == NULL) {
+ ahd_unlock(ahd, &flags);
+ return;
+ }
+
+ /*
+ * Command has not completed. Mark this
+ * SCB as having failing status prior to
+ * resetting the bus, so we get the correct
+ * error code.
+ */
+ if ((scb->flags & SCB_SENSE) != 0)
+ ahd_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ else
+ ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ ahd_reset_channel(ahd, cmd->channel + 'A', /*initiate*/TRUE);
+
+ /*
+ * Add a minimal bus settle delay for devices that are slow to
+ * respond after bus resets.
+ */
+ 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 + HZ / 2;
+ ahd->platform_data->reset_timer.function =
+ (ahd_linux_callback_t *)ahd_release_simq;
+ add_timer(&ahd->platform_data->reset_timer);
+ /*
+ * In 2.5.X, the "done lock" is the ahd_lock.
+ * Instead of dropping and re-acquiring the same
+ * lock in the 2.5.X case, just hold our ahd_lock
+ * the whole time. ahd_done_lock() has been
+ * made a no-op for 2.5.X too.
+ */
+ acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+ TAILQ_INIT(&ahd->platform_data->completeq);
+ next_dev = ahd_linux_next_device_to_run(ahd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &flags);
+#endif
+ if (next_dev)
+ ahd_schedule_runq(ahd);
+ if (acmd != NULL) {
+ acmd = ahd_linux_run_complete_queue(ahd, acmd);
+ if (acmd != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_lock(ahd, &flags);
+#endif
+ ahd_schedule_completeq(ahd, acmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &flags);
+#endif
+ }
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &flags);
+#endif
+}
+
+static void
+ahd_linux_dv_complete(struct scsi_cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+
+ ahd = *((struct ahd_softc **)cmd->host->hostdata);
+
+ /* Delete the DV timer before it goes off! */
+ scsi_delete_timer(cmd);
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("%s:%c:%d: Command completed, status= 0x%x\n",
+ ahd_name(ahd), cmd->channel, cmd->target, cmd->result);
+#endif
+
+ /* Wake up the state machine */
+ up(&ahd->platform_data->dv_cmd_sem);
+}
+
+static void
+ahd_linux_generate_dv_pattern(struct ahd_linux_target *targ)
+{
+ uint16_t b;
+ u_int i;
+ u_int j;
+
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK);
+
+ i = 0;
+
+ b = 0x0001;
+ for (j = 0 ; i < targ->dv_echo_size; j++) {
+ if (j < 32) {
+ /*
+ * 32bytes of sequential numbers.
+ */
+ targ->dv_buffer[i++] = j & 0xff;
+ } else if (j < 48) {
+ /*
+ * 32bytes of repeating 0x0000, 0xffff.
+ */
+ targ->dv_buffer[i++] = (j & 0x02) ? 0xff : 0x00;
+ } else if (j < 64) {
+ /*
+ * 32bytes of repeating 0x5555, 0xaaaa.
+ */
+ targ->dv_buffer[i++] = (j & 0x02) ? 0xaa : 0x55;
+ } else {
+ /*
+ * Remaining buffer is filled with a repeating
+ * patter of:
+ *
+ * 0xffff
+ * ~0x0001 << shifted once in each loop.
+ */
+ if (j & 0x02) {
+ if (j & 0x01) {
+ targ->dv_buffer[i++] = ~(b >> 8) & 0xff;
+ b <<= 1;
+ if (b == 0x0000)
+ b = 0x0001;
+ } else {
+ targ->dv_buffer[i++] = (~b & 0xff);
+ }
+ } else {
+ targ->dv_buffer[i++] = 0xff;
+ }
+ }
+ }
+}
+
+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);
+}
+
+static u_int
+ahd_linux_user_dv_setting(struct ahd_softc *ahd)
+{
+ static int warned_user;
+ int dv;
+
+ if (warned_user == 0
+ && ahd->unit >= NUM_ELEMENTS(aic79xx_dv_settings)) {
+
+ printf("aic79xx: WARNING, insufficient "
+ "dv settings instances for installed "
+ "controllers. Using defaults\n");
+ printf("aic79xx: Please update the "
+ "aic79xx_dv_settings array in the "
+ "aic79xx.c source file.\n");
+ dv = -1;
+ warned_user++;
+ } else {
+
+ dv = aic79xx_dv_settings[ahd->unit];
+ }
+
+ if (dv < 0) {
+ /*
+ * Apply the default.
+ */
+ dv = 1;
+ if (ahd->seep_config != 0)
+ dv = (ahd->seep_config->bios_control & CFENABLEDV);
+ }
+ return (dv);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahd_linux_device_queue_depth(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev)
+{
+ struct ahd_devinfo devinfo;
+ u_int tags;
+
+ ahd_compile_devinfo(&devinfo,
+ ahd->our_id,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+ tags = ahd_linux_user_tagdepth(ahd, &devinfo);
+ if (tags != 0
+ && dev->scsi_device != NULL
+ && dev->scsi_device->tagged_supported != 0) {
+
+ 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 {
+ ahd_set_tags(ahd, &devinfo, AHD_QUEUE_NONE);
+ }
+}
+
+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;
+ u_int col_idx;
+ 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
+ && AHD_DV_SIMQ_FROZEN(ahd) == 0) {
+
+ TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq,
+ dev, links);
+ dev->flags |= AHD_DEV_ON_RUN_LIST;
+ return;
+ }
+
+ cmd = &acmd_scsi_cmd(acmd);
+
+ /*
+ * Get an scb to use.
+ */
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ cmd->target, &tstate);
+ if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) == 0
+ || (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ col_idx = AHD_NEVER_COL_IDX;
+ } else {
+ col_idx = AHD_BUILD_COL_IDX(cmd->target, cmd->lun);
+ }
+ if ((scb = ahd_get_scb(ahd, col_idx)) == 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);
+ 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);
+
+ if ((ahd->user_discenable & mask) != 0)
+ hscb->control |= DISCENB;
+
+ if (AHD_DV_CMD(cmd) != 0)
+ scb->flags |= SCB_SILENT;
+
+ 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ int msg_bytes;
+ uint8_t tag_msgs[2];
+
+ msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs);
+ if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) {
+ hscb->control |= tag_msgs[0];
+ if (tag_msgs[0] == MSG_ORDERED_TASK)
+ dev->commands_since_idle_or_otag = 0;
+ } else
+#endif
+ 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++;
+
+ /* Update the error counting bucket and dump if needed */
+ if (dev->target->cmds_since_error) {
+ dev->target->cmds_since_error++;
+ if (dev->target->cmds_since_error >
+ AHD_LINUX_ERR_THRESH)
+ dev->target->cmds_since_error = 0;
+ }
+
+ 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);
+ /*
+ * In 2.5.X, the "done lock" is the ahd_lock.
+ * Instead of dropping and re-acquiring the same
+ * lock in the 2.5.X case, just hold our ahd_lock
+ * the whole time. ahd_done_lock() has been
+ * made a no-op for 2.5.X too.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &flags);
+#endif
+ if (next_dev)
+ ahd_schedule_runq(ahd);
+ if (acmd != NULL) {
+ acmd = ahd_linux_run_complete_queue(ahd, acmd);
+ if (acmd != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_lock(ahd, &flags);
+#endif
+ ahd_schedule_completeq(ahd, acmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &flags);
+#endif
+ }
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &flags);
+#endif
+}
+
+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);
+ while (acmd != NULL)
+ acmd = 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;
+
+ target_offset = target;
+ /*
+ * Never allow allocation of a target object for
+ * our own SCSIID.
+ */
+ if (target == ahd->our_id) {
+ ahd->platform_data->targets[target_offset] = NULL;
+ return (NULL);
+ }
+
+ targ = malloc(sizeof(*targ), M_DEVBUF, M_NOWAIT);
+ if (targ == NULL)
+ return (NULL);
+ memset(targ, 0, sizeof(*targ));
+ targ->channel = channel;
+ targ->target = target;
+ targ->ahd = ahd;
+ targ->flags = AHD_DV_REQUIRED;
+ ahd->platform_data->targets[target_offset] = targ;
+ return (targ);
+}
+
+static void
+ahd_linux_free_target(struct ahd_softc *ahd, struct ahd_linux_target *targ)
+{
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_offset;
+ char channel;
+
+ /*
+ * Force a negotiation to async/narrow on any
+ * future command to this device unless a bus
+ * reset occurs between now and that command.
+ */
+ channel = 'A' + targ->channel;
+ our_id = ahd->our_id;
+ target_offset = targ->target;
+ tinfo = ahd_fetch_transinfo(ahd, channel, our_id,
+ targ->target, &tstate);
+ ahd_compile_devinfo(&devinfo, our_id, targ->target, CAM_LUN_WILDCARD,
+ channel, ROLE_INITIATOR);
+ ahd_set_syncrate(ahd, &devinfo, 0, 0, 0,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_update_neg_request(ahd, &devinfo, tstate, tinfo, AHD_NEG_ALWAYS);
+ ahd->platform_data->targets[target_offset] = NULL;
+ if (targ->inq_data != NULL)
+ free(targ->inq_data, M_DEVBUF);
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ 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
+ && (targ->flags & AHD_DV_REQUIRED) == 0)
+ ahd_linux_free_target(ahd, targ);
+}
+
+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_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++;
+ if ((cmd->result & (CAM_DEV_QFRZN << 16)) != 0) {
+ cmd->result &= ~(CAM_DEV_QFRZN << 16);
+ dev->qfrozen--;
+ }
+ ahd_linux_unmap_scb(ahd, scb);
+
+ /*
+ * 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 ((scb->flags & SCB_TRANSMISSION_ERROR) != 0) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Set CAM_UNCOR_PARITY\n");
+ }
+#endif
+ ahd_set_transaction_status(scb, CAM_UNCOR_PARITY);
+ } else 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);
+ }
+ } else if (ahd_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) {
+ ahd_linux_handle_scsi_status(ahd, dev, scb);
+ } else if (ahd_get_transaction_status(scb) == CAM_SEL_TIMEOUT) {
+ dev->flags |= AHD_DEV_UNCONFIGURED;
+ if (AHD_DV_CMD(cmd) == FALSE)
+ dev->target->flags &= ~AHD_DV_REQUIRED;
+ }
+ /*
+ * Start DV for devices that require it assuming the first command
+ * sent does not result in a selection timeout.
+ */
+ if (ahd_get_transaction_status(scb) != CAM_SEL_TIMEOUT
+ && (dev->target->flags & AHD_DV_REQUIRED) != 0)
+ ahd_linux_start_dv(ahd);
+
+ 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");
+ if (ahd_get_transaction_status(scb) == CAM_BDR_SENT
+ || ahd_get_transaction_status(scb) == CAM_REQ_ABORTED)
+ ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ if ((scb->platform_data->flags & AHD_UP_EH_SEMAPHORE) != 0) {
+ scb->platform_data->flags &= ~AHD_UP_EH_SEMAPHORE;
+ up(&ahd->platform_data->eh_sem);
+ }
+ }
+
+ ahd_free_scb(ahd, scb);
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+
+ if ((ahd->platform_data->flags & AHD_DV_WAIT_SIMQ_EMPTY) != 0
+ && LIST_FIRST(&ahd->pending_scbs) == NULL) {
+ ahd->platform_data->flags &= ~AHD_DV_WAIT_SIMQ_EMPTY;
+ up(&ahd->platform_data->dv_sem);
+ }
+
+}
+
+static void
+ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev, struct scb *scb)
+{
+ struct ahd_devinfo devinfo;
+
+ ahd_compile_devinfo(&devinfo,
+ ahd->our_id,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+
+ /*
+ * 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_CHECK_COND:
+ case SCSI_STATUS_CMD_TERMINATED:
+ {
+ Scsi_Cmnd *cmd;
+
+ /*
+ * Copy sense information to the OS's cmd
+ * structure if it is available.
+ */
+ cmd = scb->io_ctx;
+ if ((scb->flags & (SCB_SENSE|SCB_PKT_SENSE)) != 0) {
+ struct scsi_status_iu_header *siu;
+ u_int sense_size;
+ u_int sense_offset;
+
+ if (scb->flags & SCB_SENSE) {
+ sense_size = MIN(sizeof(struct scsi_sense_data)
+ - ahd_get_sense_residual(scb),
+ sizeof(cmd->sense_buffer));
+ sense_offset = 0;
+ } else {
+ /*
+ * Copy only the sense data into the provided
+ * buffer.
+ */
+ siu = (struct scsi_status_iu_header *)
+ scb->sense_data;
+ sense_size = MIN(scsi_4btoul(siu->sense_length),
+ sizeof(cmd->sense_buffer));
+ sense_offset = SIU_SENSE_OFFSET(siu);
+ }
+
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ memcpy(cmd->sense_buffer,
+ ahd_get_sense_buf(ahd, scb)
+ + sense_offset, sense_size);
+ cmd->result |= (DRIVER_SENSE << 24);
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_SENSE) {
+ int i;
+
+ printf("Copied %d bytes of sense data at %d:",
+ sense_size, sense_offset);
+ for (i = 0; i < sense_size; i++)
+ printf(" 0x%x", cmd->sense_buffer[i]);
+ printf("\n");
+ }
+#endif
+ }
+ 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);
+ ahd_set_tags(ahd, &devinfo,
+ (dev->flags & AHD_DEV_Q_BASIC)
+ ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED);
+ break;
+ }
+ /*
+ * Drop down to a single opening, and treat this
+ * as if the target returned BUSY SCSI status.
+ */
+ dev->openings = 1;
+ ahd_set_tags(ahd, &devinfo,
+ (dev->flags & AHD_DEV_Q_BASIC)
+ ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED);
+ ahd_set_scsi_status(scb, SCSI_STATUS_BUSY);
+ /* 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_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;
+
+ /*
+ * Map CAM error codes into Linux Error codes. We
+ * avoid the conversion so that the DV code has the
+ * full error information available when making
+ * state change decisions.
+ */
+ if (AHD_DV_CMD(cmd) == FALSE) {
+ uint32_t status;
+ u_int new_status;
+
+ status = ahd_cmd_get_transaction_status(cmd);
+ if (status != CAM_REQ_CMP) {
+ struct ahd_linux_device *dev;
+ struct ahd_devinfo devinfo;
+ uint32_t action;
+
+ dev = ahd_linux_get_device(ahd, cmd->channel,
+ cmd->target, cmd->lun,
+ /*alloc*/FALSE);
+
+ if (dev == NULL)
+ goto no_fallback;
+
+ ahd_compile_devinfo(&devinfo,
+ ahd->our_id,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A':'B',
+ ROLE_INITIATOR);
+
+ action = aic_error_action(cmd, dev->target->inq_data);
+ if ((action & SSQ_FALLBACK) != 0) {
+
+ /* Update stats */
+ dev->target->errors_detected++;
+ if (dev->target->cmds_since_error == 0)
+ dev->target->cmds_since_error++;
+ else {
+ dev->target->cmds_since_error = 0;
+ ahd_linux_fallback(ahd, &devinfo);
+ }
+ }
+ }
+no_fallback:
+ switch (status) {
+ case CAM_REQ_INPROG:
+ case CAM_REQ_CMP:
+ case CAM_SCSI_STATUS_ERROR:
+ new_status = DID_OK;
+ break;
+ case CAM_REQ_ABORTED:
+ new_status = DID_ABORT;
+ break;
+ case CAM_BUSY:
+ new_status = DID_BUS_BUSY;
+ break;
+ case CAM_REQ_INVALID:
+ case CAM_PATH_INVALID:
+ new_status = DID_BAD_TARGET;
+ break;
+ case CAM_SEL_TIMEOUT:
+ new_status = DID_NO_CONNECT;
+ break;
+ case CAM_SCSI_BUS_RESET:
+ case CAM_BDR_SENT:
+ new_status = DID_RESET;
+ break;
+ case CAM_UNCOR_PARITY:
+ new_status = DID_PARITY;
+ break;
+ case CAM_CMD_TIMEOUT:
+ new_status = DID_TIME_OUT;
+ break;
+ case CAM_UA_ABORT:
+ case CAM_REQ_CMP_ERR:
+ case CAM_AUTOSENSE_FAIL:
+ case CAM_NO_HBA:
+ case CAM_DATA_RUN_ERR:
+ case CAM_UNEXP_BUSFREE:
+ case CAM_SEQUENCE_FAIL:
+ case CAM_CCB_LEN_ERR:
+ case CAM_PROVIDE_FAIL:
+ case CAM_REQ_TERMIO:
+ case CAM_UNREC_HBA_ERROR:
+ case CAM_REQ_TOO_BIG:
+ new_status = DID_ERROR;
+ break;
+ case CAM_REQUEUE_REQ:
+ /*
+ * 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. We also "fake" a check condition with
+ * a sense code of ABORTED COMMAND. This seems to
+ * evoke a retry even if this command is being sent
+ * via the eh thread. Ick! Ick! Ick!
+ */
+ cmd->retries--;
+ new_status = DID_OK;
+ ahd_cmd_set_scsi_status(cmd, SCSI_STATUS_CHECK_COND);
+ cmd->result |= (DRIVER_SENSE << 24);
+ memset(cmd->sense_buffer, 0,
+ sizeof(cmd->sense_buffer));
+ cmd->sense_buffer[0] = SSD_ERRCODE_VALID
+ | SSD_CURRENT_ERROR;
+ cmd->sense_buffer[2] = SSD_KEY_ABORTED_COMMAND;
+ break;
+ default:
+ /* We should never get here */
+ new_status = DID_ERROR;
+ break;
+ }
+
+ ahd_cmd_set_transaction_status(cmd, new_status);
+ }
+
+ 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 void
+ahd_linux_filter_inquiry(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ 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 width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+ u_int trans_version;
+ u_int prot_version;
+ static int warned_user;
+
+ /*
+ * 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, devinfo->channel - 'A',
+ devinfo->target, devinfo->lun,
+ /*alloc*/TRUE);
+
+ sid = (struct scsi_inquiry_data *)dev->target->inq_data;
+ if (SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
+
+ dev->flags &= ~AHD_DEV_UNCONFIGURED;
+ } else {
+ dev->flags |= AHD_DEV_UNCONFIGURED;
+ return;
+ }
+
+ /*
+ * Update our notion of this device's transfer
+ * negotiation capabilities.
+ */
+ 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 = MIN(user->protocol_version, SID_ANSI_REV(sid));
+
+ /*
+ * 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_osm.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;
+ }
+
+ /*
+ * Only attempt SPI3/4 once we've verified that
+ * the device claims to support SPI3/4 features.
+ */
+ if (prot_version < SCSI_REV_2)
+ trans_version = SID_ANSI_REV(sid);
+ else
+ trans_version = SCSI_REV_2;
+
+ 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;
+ }
+ if ((sid->spi3data & SID_SPI_QAS) == 0)
+ ppr_options &= ~MSG_EXT_PPR_QAS_REQ;
+ if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0)
+ ppr_options &= MSG_EXT_PPR_QAS_REQ;
+ if ((sid->spi3data & SID_SPI_IUS) == 0)
+ ppr_options &= (MSG_EXT_PPR_DT_REQ
+ | MSG_EXT_PPR_QAS_REQ);
+
+ if (prot_version > SCSI_REV_2
+ && ppr_options != 0)
+ trans_version = user->transport_version;
+
+ 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);
+}
+
+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);
+ ahd_platform_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ);
+ }
+}
+
+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;
+ }
+ if (AHD_DV_SIMQ_FROZEN(ahd)
+ && ((ahd->platform_data->flags & AHD_DV_WAIT_SIMQ_RELEASE) != 0)) {
+ ahd->platform_data->flags &= ~AHD_DV_WAIT_SIMQ_RELEASE;
+ up(&ahd->platform_data->dv_sem);
+ }
+ 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);
+
+ ahd_schedule_runq(ahd);
+}
+
+#if NOT_YET
+static void
+ahd_linux_sem_timeout(u_long arg)
+{
+ struct ahd_softc *ahd;
+ u_long s;
+
+ ahd = (struct ahd_softc *)arg;
+ ahd_lock(ahd, &s);
+ if ((ahd->platform_data->flags & AHD_UP_EH_SEMAPHORE) != 0) {
+ ahd->platform_data->flags &= ~AHD_UP_EH_SEMAPHORE;
+ up(&ahd->platform_data->eh_sem);
+ }
+ ahd_unlock(ahd, &s);
+}
+
+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;
+
+ pending_scb = NULL;
+ 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");
+
+ /*
+ * In all versions of Linux, we have to work around
+ * a major flaw in how the mid-layer is locked down
+ * if we are to sleep successfully in our error handler
+ * while allowing our interrupt handler to run. Since
+ * the midlayer acquires either the io_request_lock or
+ * our lock prior to calling us, we must use the
+ * spin_unlock_irq() method for unlocking our lock.
+ * This will force interrupts to be enabled on the
+ * current CPU. Since the EH thread should not have
+ * been running with CPU interrupts disabled other than
+ * by acquiring either the io_request_lock or our own
+ * lock, this *should* be safe.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_unlock_irq(&io_request_lock);
+#endif
+ ahd_midlayer_entrypoint_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;
+
+ pending_scb->platform_data->flags |= AHD_UP_EH_SEMAPHORE;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, &s);
+#else
+ spin_unlock_irq(ahd->platform_data->host->host_lock);
+#endif
+ init_timer(&timer);
+ timer.data = (u_long)ahd;
+ 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;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_lock(ahd, &s);
+#else
+ spin_lock_irq(ahd->platform_data->host->host_lock);
+#endif
+ }
+ acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+ TAILQ_INIT(&ahd->platform_data->completeq);
+ ahd_midlayer_entry_unlock(ahd, &s);
+ if (acmd != NULL) {
+ acmd = ahd_linux_run_complete_queue(ahd, acmd);
+ if (acmd != NULL) {
+ ahd_midlayer_entrypoint_lock(ahd, &s);
+ ahd_schedule_completeq(ahd, acmd);
+ ahd_midlayer_entrypoint_unlock(ahd, &s);
+ }
+ }
+ ahd_schedule_runq(ahd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_lock_irq(&io_request_lock);
+#endif
+ 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);
+}
+
+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);
+ }
+ }
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.h b/drivers/scsi/aic7xxx/aic79xx_osm.h
new file mode 100644
index 000000000000..46d34b111c53
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.h
@@ -0,0 +1,1281 @@
+/*
+ * 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#96 $
+ *
+ */
+#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 */
+#define AIC_LIB_PREFIX ahd
+#include "scsi.h"
+#include "hosts.h"
+#include "aiclib.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 1
+#define AHD_DEBUG_OPTS CONFIG_AIC79XX_DEBUG_MASK
+#else
+/*
+ * Compile in debugging code, but do not enable any printfs.
+ */
+#define AHD_DEBUG 1
+#endif
+/* No debugging code. */
+#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_allow_memio;
+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)
+
+/************************** Timer DataStructures ******************************/
+typedef struct timer_list ahd_timer_t;
+
+/********************************** Includes **********************************/
+#if CONFIG_AIC79XX_REG_PRETTY_PRINT
+#define AIC_DEBUG_REGISTERS 1
+#else
+#define AIC_DEBUG_REGISTERS 0
+#endif
+#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, u_int usec,
+ ahd_callback_t *func, void *arg);
+static __inline void ahd_scb_timer_reset(struct scb *scb, u_int usec);
+
+static __inline void
+ahd_timer_reset(ahd_timer_t *timer, u_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);
+}
+
+static __inline void
+ahd_scb_timer_reset(struct scb *scb, u_int usec)
+{
+ mod_timer(&scb->io_ctx->eh_timeout, jiffies + (usec * HZ)/1000000);
+}
+
+/***************************** 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.3.0.ALPHA5"
+
+/**************************** 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_SLAVE_CONFIGURED = 0x80 /* slave_configure() has been called */
+} ahd_linux_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_linux_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;
+ Scsi_Device *scsi_device;
+ struct ahd_linux_target *target;
+};
+
+typedef enum {
+ AHD_DV_REQUIRED = 0x01
+} ahd_linux_targ_flags;
+
+/* DV States */
+typedef enum {
+ AHD_DV_STATE_EXIT = 0,
+ AHD_DV_STATE_INQ_SHORT_ASYNC,
+ AHD_DV_STATE_TUR,
+ AHD_DV_STATE_INQ_ASYNC,
+ AHD_DV_STATE_INQ_ASYNC_VERIFY,
+ AHD_DV_STATE_REBD,
+ AHD_DV_STATE_INQ_VERIFY,
+ AHD_DV_STATE_WEB,
+ AHD_DV_STATE_REB,
+ AHD_DV_STATE_SU,
+ AHD_DV_STATE_BUSY
+} ahd_dv_state;
+
+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;
+ ahd_linux_targ_flags flags;
+ struct scsi_inquiry_data *inq_data;
+ /*
+ * The next "fallback" period to use for narrow/wide transfers.
+ */
+ u_int dv_next_narrow_period;
+ u_int dv_next_wide_period;
+ u_int dv_max_ppr_options;
+ u_int dv_last_ppr_options;
+ u_int dv_echo_size;
+ ahd_dv_state dv_state;
+ u_int dv_state_retry;
+ uint8_t *dv_buffer;
+ uint8_t *dv_buffer1;
+
+ /*
+ * Cumulative counter of errors.
+ */
+ u_long errors_detected;
+ u_long cmds_since_error;
+};
+
+/********************* 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.
+ */
+typedef enum {
+ AHD_UP_EH_SEMAPHORE
+} ahd_linux_scb_flags;
+
+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
+ uint32_t sense_resid; /* Auto-Sense residual */
+ ahd_linux_scb_flags flags;
+};
+
+/*
+ * 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.
+ */
+typedef enum {
+ AHD_DV_WAIT_SIMQ_EMPTY = 0x01,
+ AHD_DV_WAIT_SIMQ_RELEASE = 0x02,
+ AHD_DV_ACTIVE = 0x04,
+ AHD_DV_SHUTDOWN = 0x08,
+ AHD_RUN_CMPLT_Q_TIMER = 0x10
+} ahd_linux_softc_flags;
+
+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,93)
+ spinlock_t spin_lock;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ struct tasklet_struct runq_tasklet;
+#endif
+ u_int qfrozen;
+ pid_t dv_pid;
+ struct timer_list completeq_timer;
+ struct timer_list reset_timer;
+ struct timer_list stats_timer;
+ struct semaphore eh_sem;
+ struct semaphore dv_sem;
+ struct semaphore dv_cmd_sem; /* XXX This needs to be in
+ * the target struct
+ */
+ 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;
+ ahd_linux_softc_flags flags;
+};
+
+/************************** 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 acquisition and release of the above lock in midlayer entry points. */
+static __inline void ahd_midlayer_entrypoint_lock(struct ahd_softc *,
+ unsigned long *flags);
+static __inline void ahd_midlayer_entrypoint_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_midlayer_entrypoint_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ /*
+ * In 2.5.X, the midlayer takes our lock just before
+ * calling us, so avoid locking again.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_lock(ahd, flags);
+#endif
+}
+
+static __inline void
+ahd_midlayer_entrypoint_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ /*
+ * In 2.5.X, the midlayer takes our lock just before
+ * calling us and unlocks when we return, so let it do the unlock.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahd_unlock(ahd, flags);
+#endif
+}
+
+static __inline void
+ahd_done_lockinit(struct ahd_softc *ahd)
+{
+ /*
+ * In 2.5.X, our own lock is held during completions.
+ * In previous versions, the io_request_lock is used.
+ * In either case, we can't initialize this lock again.
+ */
+}
+
+static __inline void
+ahd_done_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ *flags = 0;
+ spin_lock_irqsave(&io_request_lock, *flags);
+#endif
+}
+
+static __inline void
+ahd_done_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_unlock_irqrestore(&io_request_lock, *flags);
+#endif
+}
+
+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
+/**************************** Proc FS Support *********************************/
+int ahd_linux_proc_info(char *, char **, off_t, int, int, int);
+
+/*************************** Domain Validation ********************************/
+#define AHD_DV_CMD(cmd) ((cmd)->scsi_done == ahd_linux_dv_complete)
+#define AHD_DV_SIMQ_FROZEN(ahd) \
+ ((((ahd)->platform_data->flags & AHD_DV_ACTIVE) != 0) \
+ && (ahd)->platform_data->qfrozen == 1)
+
+/*********************** Transaction Access Wrappers **************************/
+static __inline void ahd_cmd_set_transaction_status(Scsi_Cmnd *, uint32_t);
+static __inline void ahd_set_transaction_status(struct scb *, uint32_t);
+static __inline void ahd_cmd_set_scsi_status(Scsi_Cmnd *, uint32_t);
+static __inline void ahd_set_scsi_status(struct scb *, uint32_t);
+static __inline uint32_t ahd_cmd_get_transaction_status(Scsi_Cmnd *cmd);
+static __inline uint32_t ahd_get_transaction_status(struct scb *);
+static __inline uint32_t ahd_cmd_get_scsi_status(Scsi_Cmnd *cmd);
+static __inline uint32_t ahd_get_scsi_status(struct scb *);
+static __inline void ahd_set_transaction_tag(struct scb *, int, u_int);
+static __inline u_long ahd_get_transfer_length(struct scb *);
+static __inline int ahd_get_transfer_dir(struct scb *);
+static __inline void ahd_set_residual(struct scb *, u_long);
+static __inline void ahd_set_sense_residual(struct scb *scb, u_long resid);
+static __inline u_long ahd_get_residual(struct scb *);
+static __inline u_long ahd_get_sense_residual(struct scb *);
+static __inline int ahd_perform_autosense(struct scb *);
+static __inline uint32_t ahd_get_sense_bufsize(struct ahd_softc *,
+ struct scb *);
+static __inline void ahd_notify_xfer_settings_change(struct ahd_softc *,
+ struct ahd_devinfo *);
+static __inline void ahd_platform_scb_free(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_freeze_scb(struct scb *scb);
+
+static __inline
+void ahd_cmd_set_transaction_status(Scsi_Cmnd *cmd, uint32_t status)
+{
+ cmd->result &= ~(CAM_STATUS_MASK << 16);
+ cmd->result |= status << 16;
+}
+
+static __inline
+void ahd_set_transaction_status(struct scb *scb, uint32_t status)
+{
+ ahd_cmd_set_transaction_status(scb->io_ctx,status);
+}
+
+static __inline
+void ahd_cmd_set_scsi_status(Scsi_Cmnd *cmd, uint32_t status)
+{
+ cmd->result &= ~0xFFFF;
+ cmd->result |= status;
+}
+
+static __inline
+void ahd_set_scsi_status(struct scb *scb, uint32_t status)
+{
+ ahd_cmd_set_scsi_status(scb->io_ctx, status);
+}
+
+static __inline
+uint32_t ahd_cmd_get_transaction_status(Scsi_Cmnd *cmd)
+{
+ return ((cmd->result >> 16) & CAM_STATUS_MASK);
+}
+
+static __inline
+uint32_t ahd_get_transaction_status(struct scb *scb)
+{
+ return (ahd_cmd_get_transaction_status(scb->io_ctx));
+}
+
+static __inline
+uint32_t ahd_cmd_get_scsi_status(Scsi_Cmnd *cmd)
+{
+ return (cmd->result & 0xFFFF);
+}
+
+static __inline
+uint32_t ahd_get_scsi_status(struct scb *scb)
+{
+ return (ahd_cmd_get_scsi_status(scb->io_ctx));
+}
+
+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 *scb)
+{
+ return (scb->platform_data->xfer_len);
+}
+
+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 *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 *scb, u_long resid)
+{
+ scb->platform_data->sense_resid = resid;
+}
+
+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
+u_long ahd_get_sense_residual(struct scb *scb)
+{
+ return (scb->platform_data->sense_resid);
+}
+
+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 *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)
+{
+ 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_init(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)
+{
+ if ((scb->io_ctx->result & (CAM_DEV_QFRZN << 16)) == 0) {
+ scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
+ scb->platform_data->dev->qfrozen++;
+ }
+}
+
+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/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
new file mode 100644
index 000000000000..fe598eba390d
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
@@ -0,0 +1,431 @@
+/*
+ * 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#18 $
+ */
+
+#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 *)pci_get_drvdata(pdev));
+ 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)
+ pci_set_drvdata(pdev, 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 (aic79xx_allow_memio == 0)
+ return (ENOMEM);
+
+ if ((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);
+
+ if (ahd_pci_test_register_access(ahd) != 0) {
+
+ printf("aic79xx: PCI Device %d:%d:%d "
+ "failed memory mapped test. Using PIO.\n",
+ ahd_get_pci_bus(ahd->dev_softc),
+ ahd_get_pci_slot(ahd->dev_softc),
+ ahd_get_pci_function(ahd->dev_softc));
+ iounmap((void *)((u_long)maddr & PAGE_MASK));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ release_mem_region(ahd->platform_data->mem_busaddr,
+ 0x1000);
+#endif
+ ahd->bshs[0].maddr = NULL;
+ maddr = NULL;
+ } else
+ command |= PCIM_CMD_MEMEN;
+ } else if (bootverbose) {
+ 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/drivers/scsi/aic7xxx/aic79xx_pci.c b/drivers/scsi/aic7xxx/aic79xx_pci.c
new file mode 100644
index 000000000000..7dd68a3746fd
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_pci.c
@@ -0,0 +1,912 @@
+/*
+ * Product specific probe and attach routines for:
+ * aic7901 and aic7902 SCSI controllers
+ *
+ * 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_pci.c#60 $
+ *
+ * $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_AIC7901A 0x801E9005FFFF9005ull
+#define ID_AIC7901A_IROC 0x809E9005FFFF9005ull
+#define ID_AHA_29320A 0x8000900500609005ull
+#define ID_AHA_29320LP 0x8014900500449005ull
+#define ID_AHA_29320LP_IROC 0x8094900500449005ull
+
+#define ID_AIC7902 0x801F9005FFFF9005ull
+#define ID_AIC7902_IROC 0x809F9005FFFF9005ull
+#define ID_AIC7902_B 0x801D9005FFFF9005ull
+#define ID_AIC7902_B_IROC 0x809D9005FFFF9005ull
+#define ID_AHA_39320 0x8010900500409005ull
+#define ID_AHA_39320D 0x8011900500419005ull
+#define ID_AHA_39320D_B 0x801C900500419005ull
+#define ID_AHA_39320D_HP 0x8011900500AC0E11ull
+#define ID_AHA_39320D_B_HP 0x801C900500AC0E11ull
+#define ID_AHA_29320 0x8012900500429005ull
+#define ID_AHA_29320B 0x8013900500439005ull
+#define ID_AIC7902_PCI_REV_A4 0x3
+#define ID_AIC7902_PCI_REV_B0 0x10
+#define SUBID_HP 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_aic7901A_setup;
+static ahd_device_setup_t ahd_aic7902_setup;
+
+struct ahd_pci_identity ahd_pci_ident_table [] =
+{
+ /* aic7901A based controllers */
+ {
+ ID_AHA_29320LP,
+ ID_ALL_MASK,
+ "Adaptec 29320LP Ultra320 SCSI adapter",
+ ahd_aic7901A_setup
+ },
+ {
+ ID_AHA_29320A,
+ ID_ALL_MASK,
+ "Adaptec 29320A Ultra320 SCSI adapter",
+ ahd_aic7901A_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_HP,
+ ID_ALL_MASK,
+ "Adaptec (HP OEM) 39320D Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320D_B,
+ ID_ALL_MASK,
+ "Adaptec 39320D Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320D_B_HP,
+ ID_ALL_MASK,
+ "Adaptec (HP OEM) 39320D Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_29320,
+ ID_ALL_MASK,
+ "Adaptec 29320 Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_29320B,
+ ID_ALL_MASK,
+ "Adaptec 29320B Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ /* Generic chip probes for devices we don't know 'exactly' */
+ {
+ ID_AIC7901A & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec AIC7901A Ultra320 SCSI adapter",
+ ahd_aic7901A_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_int command;
+ uint32_t devconfig;
+ uint16_t subvendor;
+ int error;
+
+ shared_scb_data = NULL;
+ ahd->description = entry->name;
+ /*
+ * Record if this is an HP board.
+ */
+ subvendor = ahd_pci_read_config(ahd->dev_softc,
+ PCIR_SUBVEND_0, /*bytes*/2);
+ if (subvendor == SUBID_HP)
+ ahd->flags |= AHD_HP_BOARD;
+
+ error = entry->setup(ahd);
+ if (error != 0)
+ return (error);
+
+ 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)];
+
+ 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_list_unlock(&l);
+ return (0);
+}
+
+/*
+ * Perform some simple tests that should catch situations where
+ * our registers are invalidly mapped.
+ */
+int
+ahd_pci_test_register_access(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ int error;
+ uint8_t seqctl;
+
+ saved_modes = ahd_save_modes(ahd);
+ error = EIO;
+
+ /* Enable PCI error interrupt status */
+ seqctl = ahd_inb(ahd, SEQCTL0);
+ ahd_outb(ahd, SEQCTL0, seqctl & ~FAILDIS);
+
+ /*
+ * First a simple test to see if any
+ * registers can be read. Reading
+ * HCNTRL has no side effects and has
+ * at least one bit that is guaranteed to
+ * be zero so it is a good register to
+ * use for this test.
+ */
+ if (ahd_inb(ahd, HCNTRL) == 0xFF)
+ goto fail;
+
+ /*
+ * Next create a situation where write combining
+ * or read prefetching could be initiated by the
+ * CPU or host bridge. Our device does not support
+ * either, so look for data corruption and/or flaged
+ * PCI errors.
+ */
+ ahd_outl(ahd, SRAM_BASE, 0x5aa555aa);
+ if (ahd_inl(ahd, SRAM_BASE) != 0x5aa555aa)
+ goto fail;
+
+ if ((ahd_inb(ahd, INTSTAT) & PCIINT) != 0) {
+ u_int targpcistat;
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ targpcistat = ahd_inb(ahd, TARGPCISTAT);
+ if ((targpcistat & STA) != 0)
+ goto fail;
+ }
+
+ error = 0;
+
+fail:
+ if ((ahd_inb(ahd, INTSTAT) & PCIINT) != 0) {
+ u_int targpcistat;
+ u_int pci_status1;
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ targpcistat = ahd_inb(ahd, TARGPCISTAT);
+
+ /* Silently clear any latched errors. */
+ ahd_outb(ahd, TARGPCISTAT, targpcistat);
+ 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_outb(ahd, CLRINT, CLRPCIINT);
+ }
+
+ ahd_restore_modes(ahd, saved_modes);
+ ahd_outb(ahd, SEQCTL0, seqctl);
+ return (error);
+}
+
+/*
+ * 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);
+ 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;
+ }
+ }
+
+#if 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;
+ if (bootverbose)
+ printf("%s: STPWLEVEL is %s\n",
+ ahd_name(ahd), (devconfig & STPWLEVEL) ? "on" : "off");
+ 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: Signaled 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_outb(ahd, CLRINT, CLRPCIINT);
+ 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_outb(ahd, CLRINT, CLRSPLTINT);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+static int
+ahd_aic7901A_setup(struct ahd_softc *ahd)
+{
+ int error;
+
+ error = ahd_aic7902_setup(ahd);
+ if (error != 0)
+ return (error);
+ ahd->chip = AHD_AIC7901A;
+ return (0);
+}
+
+static int
+ahd_aic7902_setup(struct ahd_softc *ahd)
+{
+ ahd_dev_softc_t pci;
+ u_int rev;
+
+ pci = ahd->dev_softc;
+ rev = ahd_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev < ID_AIC7902_PCI_REV_A4) {
+ 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);
+ }
+ ahd->channel = ahd_get_pci_function(pci) + 'A';
+ ahd->chip = AHD_AIC7902;
+ ahd->features = AHD_AIC7902_FE;
+ if (rev < ID_AIC7902_PCI_REV_B0) {
+ /*
+ * 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
+ | AHD_PKT_LUN_BUG|AHD_MDFF_WSCBPTR_BUG
+ | AHD_REG_SLOW_SETTLE_BUG|AHD_SET_MODE_BUG
+ | AHD_BUSFREEREV_BUG|AHD_NONPACKFIFO_BUG
+ | AHD_PACED_NEGTABLE_BUG;
+
+ /*
+ * IO Cell paramter setup.
+ */
+ AHD_SET_PRECOMP(ahd, AHD_PRECOMP_CUTBACK_29);
+
+ if ((ahd->flags & AHD_HP_BOARD) == 0)
+ AHD_SET_SLEWRATE(ahd, AHD_SLEWRATE_DEF_REVA);
+ } else {
+ u_int devconfig1;
+
+ ahd->features |= AHD_RTI|AHD_NEW_IOCELL_OPTS
+ | AHD_NEW_DFCNTRL_OPTS;
+ ahd->bugs |= AHD_LQOOVERRUN_BUG|AHD_ABORT_LQI_BUG
+ | AHD_INTCOLLISION_BUG;
+
+ /*
+ * IO Cell paramter setup.
+ */
+ AHD_SET_PRECOMP(ahd, AHD_PRECOMP_CUTBACK_29);
+ AHD_SET_SLEWRATE(ahd, AHD_SLEWRATE_DEF_REVB);
+ AHD_SET_AMPLITUDE(ahd, AHD_AMPLITUDE_DEF);
+
+ /*
+ * Set the PREQDIS bit for H2B which disables some workaround
+ * that doesn't work on regular PCI busses.
+ * XXX - Find out exactly what this does from the hardware
+ * folks!
+ */
+ 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);
+ }
+
+ return (0);
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_proc.c b/drivers/scsi/aic7xxx/aic79xx_proc.c
new file mode 100644
index 000000000000..85d6ba27b147
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_proc.c
@@ -0,0 +1,345 @@
+/*
+ * 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#11 $
+ */
+#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 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);
+}
+
+void
+ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo)
+{
+ u_int speed;
+ u_int freq;
+ u_int mb;
+
+ if (tinfo->period == AHD_PERIOD_UNKNOWN) {
+ copy_info(info, "Renegotiation Pending\n");
+ return;
+ }
+ speed = 3300;
+ freq = 0;
+ if (tinfo->offset != 0) {
+ freq = aic_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) {
+ int printed_options;
+
+ printed_options = 0;
+ copy_info(info, " (%d.%03dMHz", freq / 1000, freq % 1000);
+ if ((tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+ copy_info(info, " DT");
+ printed_options++;
+ }
+ if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ copy_info(info, "%s", printed_options ? "|IU" : " IU");
+ printed_options++;
+ }
+ if ((tinfo->ppr_options & MSG_EXT_PPR_RTI) != 0) {
+ copy_info(info, "%s",
+ printed_options ? "|RTI" : " RTI");
+ printed_options++;
+ }
+ if ((tinfo->ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) {
+ copy_info(info, "%s",
+ printed_options ? "|QAS" : " QAS");
+ printed_options++;
+ }
+ }
+
+ 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);
+ copy_info(info, "\tTransmission Errors %ld\n", targ->errors_detected);
+
+ 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/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped b/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
new file mode 100644
index 000000000000..72079fdfdbbb
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
@@ -0,0 +1,3776 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#77 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#59 $
+ */
+typedef int (ahd_reg_print_t)(u_int, u_int *, u_int);
+typedef struct ahd_reg_parse_entry {
+ char *name;
+ uint8_t value;
+ uint8_t mask;
+} ahd_reg_parse_entry_t;
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_mode_ptr_print;
+#else
+#define ahd_mode_ptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MODE_PTR", 0x00, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intstat_print;
+#else
+#define ahd_intstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INTSTAT", 0x01, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintcode_print;
+#else
+#define ahd_seqintcode_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQINTCODE", 0x02, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrint_print;
+#else
+#define ahd_clrint_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRINT", 0x03, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_error_print;
+#else
+#define ahd_error_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ERROR", 0x04, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrerr_print;
+#else
+#define ahd_clrerr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRERR", 0x04, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hcntrl_print;
+#else
+#define ahd_hcntrl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HCNTRL", 0x05, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hnscb_qoff_print;
+#else
+#define ahd_hnscb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HNSCB_QOFF", 0x06, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hescb_qoff_print;
+#else
+#define ahd_hescb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HESCB_QOFF", 0x08, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hs_mailbox_print;
+#else
+#define ahd_hs_mailbox_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HS_MAILBOX", 0x0b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrseqintstat_print;
+#else
+#define ahd_clrseqintstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSEQINTSTAT", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintstat_print;
+#else
+#define ahd_seqintstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQINTSTAT", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_swtimer_print;
+#else
+#define ahd_swtimer_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SWTIMER", 0x0e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_snscb_qoff_print;
+#else
+#define ahd_snscb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SNSCB_QOFF", 0x10, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sescb_qoff_print;
+#else
+#define ahd_sescb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SESCB_QOFF", 0x12, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sdscb_qoff_print;
+#else
+#define ahd_sdscb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SDSCB_QOFF", 0x14, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qoff_ctlsta_print;
+#else
+#define ahd_qoff_ctlsta_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QOFF_CTLSTA", 0x16, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intctl_print;
+#else
+#define ahd_intctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INTCTL", 0x18, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfcntrl_print;
+#else
+#define ahd_dfcntrl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFCNTRL", 0x19, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dscommand0_print;
+#else
+#define ahd_dscommand0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSCOMMAND0", 0x19, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfstatus_print;
+#else
+#define ahd_dfstatus_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFSTATUS", 0x1a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sg_cache_shadow_print;
+#else
+#define ahd_sg_cache_shadow_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SG_CACHE_SHADOW", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_arbctl_print;
+#else
+#define ahd_arbctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ARBCTL", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sg_cache_pre_print;
+#else
+#define ahd_sg_cache_pre_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SG_CACHE_PRE", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqin_print;
+#else
+#define ahd_lqin_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQIN", 0x20, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_typeptr_print;
+#else
+#define ahd_typeptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TYPEPTR", 0x20, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_tagptr_print;
+#else
+#define ahd_tagptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TAGPTR", 0x21, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lunptr_print;
+#else
+#define ahd_lunptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LUNPTR", 0x22, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_datalenptr_print;
+#else
+#define ahd_datalenptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DATALENPTR", 0x23, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_statlenptr_print;
+#else
+#define ahd_statlenptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "STATLENPTR", 0x24, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdlenptr_print;
+#else
+#define ahd_cmdlenptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDLENPTR", 0x25, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_attrptr_print;
+#else
+#define ahd_attrptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ATTRPTR", 0x26, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flagptr_print;
+#else
+#define ahd_flagptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLAGPTR", 0x27, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdptr_print;
+#else
+#define ahd_cmdptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDPTR", 0x28, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qnextptr_print;
+#else
+#define ahd_qnextptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QNEXTPTR", 0x29, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_idptr_print;
+#else
+#define ahd_idptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "IDPTR", 0x2a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_abrtbyteptr_print;
+#else
+#define ahd_abrtbyteptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ABRTBYTEPTR", 0x2b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_abrtbitptr_print;
+#else
+#define ahd_abrtbitptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ABRTBITPTR", 0x2c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmdbytes_print;
+#else
+#define ahd_maxcmdbytes_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MAXCMDBYTES", 0x2d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmd2rcv_print;
+#else
+#define ahd_maxcmd2rcv_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MAXCMD2RCV", 0x2e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_shortthresh_print;
+#else
+#define ahd_shortthresh_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SHORTTHRESH", 0x2f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lunlen_print;
+#else
+#define ahd_lunlen_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LUNLEN", 0x30, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cdblimit_print;
+#else
+#define ahd_cdblimit_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CDBLIMIT", 0x31, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmd_print;
+#else
+#define ahd_maxcmd_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MAXCMD", 0x32, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmdcnt_print;
+#else
+#define ahd_maxcmdcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MAXCMDCNT", 0x33, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqrsvd01_print;
+#else
+#define ahd_lqrsvd01_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQRSVD01", 0x34, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqrsvd16_print;
+#else
+#define ahd_lqrsvd16_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQRSVD16", 0x35, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqrsvd17_print;
+#else
+#define ahd_lqrsvd17_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQRSVD17", 0x36, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdrsvd0_print;
+#else
+#define ahd_cmdrsvd0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDRSVD0", 0x37, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqctl0_print;
+#else
+#define ahd_lqctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQCTL0", 0x38, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqctl1_print;
+#else
+#define ahd_lqctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQCTL1", 0x38, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsbist0_print;
+#else
+#define ahd_scsbist0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSBIST0", 0x39, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqctl2_print;
+#else
+#define ahd_lqctl2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQCTL2", 0x39, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsbist1_print;
+#else
+#define ahd_scsbist1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSBIST1", 0x3a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiseq0_print;
+#else
+#define ahd_scsiseq0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISEQ0", 0x3a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiseq1_print;
+#else
+#define ahd_scsiseq1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISEQ1", 0x3b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sxfrctl0_print;
+#else
+#define ahd_sxfrctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SXFRCTL0", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_businitid_print;
+#else
+#define ahd_businitid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BUSINITID", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dlcount_print;
+#else
+#define ahd_dlcount_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DLCOUNT", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sxfrctl1_print;
+#else
+#define ahd_sxfrctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SXFRCTL1", 0x3d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_bustargid_print;
+#else
+#define ahd_bustargid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BUSTARGID", 0x3e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sxfrctl2_print;
+#else
+#define ahd_sxfrctl2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SXFRCTL2", 0x3e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dffstat_print;
+#else
+#define ahd_dffstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFFSTAT", 0x3f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsisigo_print;
+#else
+#define ahd_scsisigo_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISIGO", 0x40, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_multargid_print;
+#else
+#define ahd_multargid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MULTARGID", 0x40, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsisigi_print;
+#else
+#define ahd_scsisigi_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISIGI", 0x41, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiphase_print;
+#else
+#define ahd_scsiphase_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSIPHASE", 0x42, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsidat0_img_print;
+#else
+#define ahd_scsidat0_img_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSIDAT0_IMG", 0x43, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsidat_print;
+#else
+#define ahd_scsidat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSIDAT", 0x44, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsibus_print;
+#else
+#define ahd_scsibus_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSIBUS", 0x46, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_targidin_print;
+#else
+#define ahd_targidin_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TARGIDIN", 0x48, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_selid_print;
+#else
+#define ahd_selid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SELID", 0x49, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sblkctl_print;
+#else
+#define ahd_sblkctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SBLKCTL", 0x4a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_optionmode_print;
+#else
+#define ahd_optionmode_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OPTIONMODE", 0x4a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat0_print;
+#else
+#define ahd_sstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SSTAT0", 0x4b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint0_print;
+#else
+#define ahd_clrsint0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSINT0", 0x4b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode0_print;
+#else
+#define ahd_simode0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SIMODE0", 0x4b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint1_print;
+#else
+#define ahd_clrsint1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSINT1", 0x4c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat1_print;
+#else
+#define ahd_sstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SSTAT1", 0x4c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat2_print;
+#else
+#define ahd_sstat2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SSTAT2", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint2_print;
+#else
+#define ahd_clrsint2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSINT2", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode2_print;
+#else
+#define ahd_simode2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SIMODE2", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_perrdiag_print;
+#else
+#define ahd_perrdiag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PERRDIAG", 0x4e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistate_print;
+#else
+#define ahd_lqistate_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQISTATE", 0x4e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_soffcnt_print;
+#else
+#define ahd_soffcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SOFFCNT", 0x4f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostate_print;
+#else
+#define ahd_lqostate_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSTATE", 0x4f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistat0_print;
+#else
+#define ahd_lqistat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQISTAT0", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqiint0_print;
+#else
+#define ahd_clrlqiint0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRLQIINT0", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqimode0_print;
+#else
+#define ahd_lqimode0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQIMODE0", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqimode1_print;
+#else
+#define ahd_lqimode1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQIMODE1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistat1_print;
+#else
+#define ahd_lqistat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQISTAT1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqiint1_print;
+#else
+#define ahd_clrlqiint1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRLQIINT1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistat2_print;
+#else
+#define ahd_lqistat2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQISTAT2", 0x52, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat3_print;
+#else
+#define ahd_sstat3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SSTAT3", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode3_print;
+#else
+#define ahd_simode3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SIMODE3", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint3_print;
+#else
+#define ahd_clrsint3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSINT3", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqomode0_print;
+#else
+#define ahd_lqomode0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOMODE0", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostat0_print;
+#else
+#define ahd_lqostat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSTAT0", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqoint0_print;
+#else
+#define ahd_clrlqoint0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRLQOINT0", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostat1_print;
+#else
+#define ahd_lqostat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSTAT1", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqoint1_print;
+#else
+#define ahd_clrlqoint1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRLQOINT1", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqomode1_print;
+#else
+#define ahd_lqomode1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOMODE1", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostat2_print;
+#else
+#define ahd_lqostat2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSTAT2", 0x56, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_os_space_cnt_print;
+#else
+#define ahd_os_space_cnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OS_SPACE_CNT", 0x56, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode1_print;
+#else
+#define ahd_simode1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SIMODE1", 0x57, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_gsfifo_print;
+#else
+#define ahd_gsfifo_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "GSFIFO", 0x58, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dffsxfrctl_print;
+#else
+#define ahd_dffsxfrctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFFSXFRCTL", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqoscsctl_print;
+#else
+#define ahd_lqoscsctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSCSCTL", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_nextscb_print;
+#else
+#define ahd_nextscb_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEXTSCB", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrseqintsrc_print;
+#else
+#define ahd_clrseqintsrc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSEQINTSRC", 0x5b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintsrc_print;
+#else
+#define ahd_seqintsrc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQINTSRC", 0x5b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_currscb_print;
+#else
+#define ahd_currscb_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CURRSCB", 0x5c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqimode_print;
+#else
+#define ahd_seqimode_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQIMODE", 0x5c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_mdffstat_print;
+#else
+#define ahd_mdffstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MDFFSTAT", 0x5d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_crccontrol_print;
+#else
+#define ahd_crccontrol_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CRCCONTROL", 0x5d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfftag_print;
+#else
+#define ahd_dfftag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFFTAG", 0x5e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lastscb_print;
+#else
+#define ahd_lastscb_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LASTSCB", 0x5e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsitest_print;
+#else
+#define ahd_scsitest_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSITEST", 0x5e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_iopdnctl_print;
+#else
+#define ahd_iopdnctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "IOPDNCTL", 0x5f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_shaddr_print;
+#else
+#define ahd_shaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SHADDR", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negoaddr_print;
+#else
+#define ahd_negoaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGOADDR", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dgrpcrci_print;
+#else
+#define ahd_dgrpcrci_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DGRPCRCI", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negperiod_print;
+#else
+#define ahd_negperiod_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGPERIOD", 0x61, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_packcrci_print;
+#else
+#define ahd_packcrci_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PACKCRCI", 0x62, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negoffset_print;
+#else
+#define ahd_negoffset_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGOFFSET", 0x62, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negppropts_print;
+#else
+#define ahd_negppropts_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGPPROPTS", 0x63, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negconopts_print;
+#else
+#define ahd_negconopts_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGCONOPTS", 0x64, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_annexcol_print;
+#else
+#define ahd_annexcol_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ANNEXCOL", 0x65, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scschkn_print;
+#else
+#define ahd_scschkn_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSCHKN", 0x66, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_annexdat_print;
+#else
+#define ahd_annexdat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ANNEXDAT", 0x66, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_iownid_print;
+#else
+#define ahd_iownid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "IOWNID", 0x67, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll960ctl0_print;
+#else
+#define ahd_pll960ctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL960CTL0", 0x68, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_shcnt_print;
+#else
+#define ahd_shcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SHCNT", 0x68, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_townid_print;
+#else
+#define ahd_townid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TOWNID", 0x69, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll960ctl1_print;
+#else
+#define ahd_pll960ctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL960CTL1", 0x69, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll960cnt0_print;
+#else
+#define ahd_pll960cnt0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL960CNT0", 0x6a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_xsig_print;
+#else
+#define ahd_xsig_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "XSIG", 0x6a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seloid_print;
+#else
+#define ahd_seloid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SELOID", 0x6b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll400ctl0_print;
+#else
+#define ahd_pll400ctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL400CTL0", 0x6c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_fairness_print;
+#else
+#define ahd_fairness_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FAIRNESS", 0x6c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll400ctl1_print;
+#else
+#define ahd_pll400ctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL400CTL1", 0x6d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll400cnt0_print;
+#else
+#define ahd_pll400cnt0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL400CNT0", 0x6e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_unfairness_print;
+#else
+#define ahd_unfairness_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "UNFAIRNESS", 0x6e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_haddr_print;
+#else
+#define ahd_haddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HADDR", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_plldelay_print;
+#else
+#define ahd_plldelay_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLLDELAY", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hodmaadr_print;
+#else
+#define ahd_hodmaadr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HODMAADR", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hodmacnt_print;
+#else
+#define ahd_hodmacnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HODMACNT", 0x78, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hcnt_print;
+#else
+#define ahd_hcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HCNT", 0x78, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hodmaen_print;
+#else
+#define ahd_hodmaen_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HODMAEN", 0x7a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sghaddr_print;
+#else
+#define ahd_sghaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGHADDR", 0x7c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbhaddr_print;
+#else
+#define ahd_scbhaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBHADDR", 0x7c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sghcnt_print;
+#else
+#define ahd_sghcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGHCNT", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbhcnt_print;
+#else
+#define ahd_scbhcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBHCNT", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dff_thrsh_print;
+#else
+#define ahd_dff_thrsh_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFF_THRSH", 0x88, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_romaddr_print;
+#else
+#define ahd_romaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ROMADDR", 0x8a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_romcntrl_print;
+#else
+#define ahd_romcntrl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ROMCNTRL", 0x8d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_romdata_print;
+#else
+#define ahd_romdata_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ROMDATA", 0x8e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcrxmsg0_print;
+#else
+#define ahd_cmcrxmsg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCRXMSG0", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_roenable_print;
+#else
+#define ahd_roenable_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ROENABLE", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyrxmsg0_print;
+#else
+#define ahd_ovlyrxmsg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYRXMSG0", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchrxmsg0_print;
+#else
+#define ahd_dchrxmsg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHRXMSG0", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyrxmsg1_print;
+#else
+#define ahd_ovlyrxmsg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYRXMSG1", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_nsenable_print;
+#else
+#define ahd_nsenable_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NSENABLE", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchrxmsg1_print;
+#else
+#define ahd_dchrxmsg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHRXMSG1", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcrxmsg1_print;
+#else
+#define ahd_cmcrxmsg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCRXMSG1", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchrxmsg2_print;
+#else
+#define ahd_dchrxmsg2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHRXMSG2", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyrxmsg2_print;
+#else
+#define ahd_ovlyrxmsg2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYRXMSG2", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcrxmsg2_print;
+#else
+#define ahd_cmcrxmsg2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCRXMSG2", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ost_print;
+#else
+#define ahd_ost_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OST", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchrxmsg3_print;
+#else
+#define ahd_dchrxmsg3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHRXMSG3", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcrxmsg3_print;
+#else
+#define ahd_cmcrxmsg3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCRXMSG3", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pcixctl_print;
+#else
+#define ahd_pcixctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PCIXCTL", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyrxmsg3_print;
+#else
+#define ahd_ovlyrxmsg3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYRXMSG3", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyseqbcnt_print;
+#else
+#define ahd_ovlyseqbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYSEQBCNT", 0x94, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcseqbcnt_print;
+#else
+#define ahd_cmcseqbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCSEQBCNT", 0x94, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchseqbcnt_print;
+#else
+#define ahd_dchseqbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHSEQBCNT", 0x94, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcspltstat0_print;
+#else
+#define ahd_cmcspltstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCSPLTSTAT0", 0x96, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyspltstat0_print;
+#else
+#define ahd_ovlyspltstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYSPLTSTAT0", 0x96, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchspltstat0_print;
+#else
+#define ahd_dchspltstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHSPLTSTAT0", 0x96, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchspltstat1_print;
+#else
+#define ahd_dchspltstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHSPLTSTAT1", 0x97, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcspltstat1_print;
+#else
+#define ahd_cmcspltstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCSPLTSTAT1", 0x97, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyspltstat1_print;
+#else
+#define ahd_ovlyspltstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYSPLTSTAT1", 0x97, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgrxmsg0_print;
+#else
+#define ahd_sgrxmsg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGRXMSG0", 0x98, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutadr0_print;
+#else
+#define ahd_slvspltoutadr0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTADR0", 0x98, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgrxmsg1_print;
+#else
+#define ahd_sgrxmsg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGRXMSG1", 0x99, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutadr1_print;
+#else
+#define ahd_slvspltoutadr1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTADR1", 0x99, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgrxmsg2_print;
+#else
+#define ahd_sgrxmsg2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGRXMSG2", 0x9a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutadr2_print;
+#else
+#define ahd_slvspltoutadr2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTADR2", 0x9a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgrxmsg3_print;
+#else
+#define ahd_sgrxmsg3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGRXMSG3", 0x9b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutadr3_print;
+#else
+#define ahd_slvspltoutadr3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTADR3", 0x9b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgseqbcnt_print;
+#else
+#define ahd_sgseqbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGSEQBCNT", 0x9c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutattr0_print;
+#else
+#define ahd_slvspltoutattr0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTATTR0", 0x9c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutattr1_print;
+#else
+#define ahd_slvspltoutattr1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTATTR1", 0x9d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutattr2_print;
+#else
+#define ahd_slvspltoutattr2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTATTR2", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgspltstat0_print;
+#else
+#define ahd_sgspltstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGSPLTSTAT0", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sfunct_print;
+#else
+#define ahd_sfunct_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SFUNCT", 0x9f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgspltstat1_print;
+#else
+#define ahd_sgspltstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGSPLTSTAT1", 0x9f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_df0pcistat_print;
+#else
+#define ahd_df0pcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DF0PCISTAT", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_reg0_print;
+#else
+#define ahd_reg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "REG0", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_df1pcistat_print;
+#else
+#define ahd_df1pcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DF1PCISTAT", 0xa1, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgpcistat_print;
+#else
+#define ahd_sgpcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGPCISTAT", 0xa2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_reg1_print;
+#else
+#define ahd_reg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "REG1", 0xa2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcpcistat_print;
+#else
+#define ahd_cmcpcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCPCISTAT", 0xa3, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlypcistat_print;
+#else
+#define ahd_ovlypcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYPCISTAT", 0xa4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_reg_isr_print;
+#else
+#define ahd_reg_isr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "REG_ISR", 0xa4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sg_state_print;
+#else
+#define ahd_sg_state_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SG_STATE", 0xa6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_msipcistat_print;
+#else
+#define ahd_msipcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MSIPCISTAT", 0xa6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_targpcistat_print;
+#else
+#define ahd_targpcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TARGPCISTAT", 0xa7, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_data_count_odd_print;
+#else
+#define ahd_data_count_odd_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DATA_COUNT_ODD", 0xa7, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbptr_print;
+#else
+#define ahd_scbptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBPTR", 0xa8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbacnt_print;
+#else
+#define ahd_ccscbacnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBACNT", 0xab, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbautoptr_print;
+#else
+#define ahd_scbautoptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBAUTOPTR", 0xab, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccsgaddr_print;
+#else
+#define ahd_ccsgaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSGADDR", 0xac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbaddr_print;
+#else
+#define ahd_ccscbaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBADDR", 0xac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbadr_bk_print;
+#else
+#define ahd_ccscbadr_bk_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBADR_BK", 0xac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmc_rambist_print;
+#else
+#define ahd_cmc_rambist_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMC_RAMBIST", 0xad, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccsgctl_print;
+#else
+#define ahd_ccsgctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSGCTL", 0xad, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbctl_print;
+#else
+#define ahd_ccscbctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBCTL", 0xad, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccsgram_print;
+#else
+#define ahd_ccsgram_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSGRAM", 0xb0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flexadr_print;
+#else
+#define ahd_flexadr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLEXADR", 0xb0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbram_print;
+#else
+#define ahd_ccscbram_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBRAM", 0xb0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flexcnt_print;
+#else
+#define ahd_flexcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLEXCNT", 0xb3, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flexdmastat_print;
+#else
+#define ahd_flexdmastat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLEXDMASTAT", 0xb5, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flexdata_print;
+#else
+#define ahd_flexdata_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLEXDATA", 0xb6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_brddat_print;
+#else
+#define ahd_brddat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BRDDAT", 0xb8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_brdctl_print;
+#else
+#define ahd_brdctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BRDCTL", 0xb9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seeadr_print;
+#else
+#define ahd_seeadr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEEADR", 0xba, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seedat_print;
+#else
+#define ahd_seedat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEEDAT", 0xbc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seectl_print;
+#else
+#define ahd_seectl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEECTL", 0xbe, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seestat_print;
+#else
+#define ahd_seestat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEESTAT", 0xbe, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbcnt_print;
+#else
+#define ahd_scbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBCNT", 0xbf, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfwaddr_print;
+#else
+#define ahd_dfwaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFWADDR", 0xc0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspfltrctl_print;
+#else
+#define ahd_dspfltrctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPFLTRCTL", 0xc0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspdatactl_print;
+#else
+#define ahd_dspdatactl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPDATACTL", 0xc1, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfraddr_print;
+#else
+#define ahd_dfraddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFRADDR", 0xc2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspreqctl_print;
+#else
+#define ahd_dspreqctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPREQCTL", 0xc2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspackctl_print;
+#else
+#define ahd_dspackctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPACKCTL", 0xc3, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfdat_print;
+#else
+#define ahd_dfdat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFDAT", 0xc4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspselect_print;
+#else
+#define ahd_dspselect_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPSELECT", 0xc4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_wrtbiasctl_print;
+#else
+#define ahd_wrtbiasctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WRTBIASCTL", 0xc5, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_rcvrbiosctl_print;
+#else
+#define ahd_rcvrbiosctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "RCVRBIOSCTL", 0xc6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_wrtbiascalc_print;
+#else
+#define ahd_wrtbiascalc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WRTBIASCALC", 0xc7, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfptrs_print;
+#else
+#define ahd_dfptrs_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFPTRS", 0xc8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_rcvrbiascalc_print;
+#else
+#define ahd_rcvrbiascalc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "RCVRBIASCALC", 0xc8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfbkptr_print;
+#else
+#define ahd_dfbkptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFBKPTR", 0xc9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_skewcalc_print;
+#else
+#define ahd_skewcalc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SKEWCALC", 0xc9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfdbctl_print;
+#else
+#define ahd_dfdbctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFDBCTL", 0xcb, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfscnt_print;
+#else
+#define ahd_dfscnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFSCNT", 0xcc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfbcnt_print;
+#else
+#define ahd_dfbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFBCNT", 0xce, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyaddr_print;
+#else
+#define ahd_ovlyaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYADDR", 0xd4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqctl0_print;
+#else
+#define ahd_seqctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQCTL0", 0xd6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqctl1_print;
+#else
+#define ahd_seqctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQCTL1", 0xd7, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flags_print;
+#else
+#define ahd_flags_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLAGS", 0xd8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintctl_print;
+#else
+#define ahd_seqintctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQINTCTL", 0xd9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqram_print;
+#else
+#define ahd_seqram_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQRAM", 0xda, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_prgmcnt_print;
+#else
+#define ahd_prgmcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PRGMCNT", 0xde, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_accum_print;
+#else
+#define ahd_accum_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ACCUM", 0xe0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sindex_print;
+#else
+#define ahd_sindex_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SINDEX", 0xe2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dindex_print;
+#else
+#define ahd_dindex_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DINDEX", 0xe4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_brkaddr1_print;
+#else
+#define ahd_brkaddr1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BRKADDR1", 0xe6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_brkaddr0_print;
+#else
+#define ahd_brkaddr0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BRKADDR0", 0xe6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_allones_print;
+#else
+#define ahd_allones_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ALLONES", 0xe8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_allzeros_print;
+#else
+#define ahd_allzeros_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ALLZEROS", 0xea, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_none_print;
+#else
+#define ahd_none_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NONE", 0xea, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sindir_print;
+#else
+#define ahd_sindir_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SINDIR", 0xec, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dindir_print;
+#else
+#define ahd_dindir_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DINDIR", 0xed, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_function1_print;
+#else
+#define ahd_function1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FUNCTION1", 0xf0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_stack_print;
+#else
+#define ahd_stack_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "STACK", 0xf2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_curaddr_print;
+#else
+#define ahd_curaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CURADDR", 0xf4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intvec1_addr_print;
+#else
+#define ahd_intvec1_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INTVEC1_ADDR", 0xf4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intvec2_addr_print;
+#else
+#define ahd_intvec2_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INTVEC2_ADDR", 0xf6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lastaddr_print;
+#else
+#define ahd_lastaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LASTADDR", 0xf6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_longjmp_addr_print;
+#else
+#define ahd_longjmp_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LONGJMP_ADDR", 0xf8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_longjmp_scb_print;
+#else
+#define ahd_longjmp_scb_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LONGJMP_SCB", 0xfa, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_accum_save_print;
+#else
+#define ahd_accum_save_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ACCUM_SAVE", 0xfc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_waiting_scb_tails_print;
+#else
+#define ahd_waiting_scb_tails_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WAITING_SCB_TAILS", 0x100, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ahd_pci_config_base_print;
+#else
+#define ahd_ahd_pci_config_base_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "AHD_PCI_CONFIG_BASE", 0x100, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sram_base_print;
+#else
+#define ahd_sram_base_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SRAM_BASE", 0x100, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_waiting_tid_head_print;
+#else
+#define ahd_waiting_tid_head_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WAITING_TID_HEAD", 0x120, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_waiting_tid_tail_print;
+#else
+#define ahd_waiting_tid_tail_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WAITING_TID_TAIL", 0x122, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_next_queued_scb_addr_print;
+#else
+#define ahd_next_queued_scb_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEXT_QUEUED_SCB_ADDR", 0x124, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_scb_head_print;
+#else
+#define ahd_complete_scb_head_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "COMPLETE_SCB_HEAD", 0x128, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_scb_dmainprog_head_print;
+#else
+#define ahd_complete_scb_dmainprog_head_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "COMPLETE_SCB_DMAINPROG_HEAD", 0x12a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_dma_scb_head_print;
+#else
+#define ahd_complete_dma_scb_head_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "COMPLETE_DMA_SCB_HEAD", 0x12c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qfreeze_count_print;
+#else
+#define ahd_qfreeze_count_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QFREEZE_COUNT", 0x12e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_saved_mode_print;
+#else
+#define ahd_saved_mode_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SAVED_MODE", 0x130, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_msg_out_print;
+#else
+#define ahd_msg_out_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MSG_OUT", 0x131, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dmaparams_print;
+#else
+#define ahd_dmaparams_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DMAPARAMS", 0x132, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seq_flags_print;
+#else
+#define ahd_seq_flags_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQ_FLAGS", 0x133, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_saved_scsiid_print;
+#else
+#define ahd_saved_scsiid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SAVED_SCSIID", 0x134, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_saved_lun_print;
+#else
+#define ahd_saved_lun_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SAVED_LUN", 0x135, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lastphase_print;
+#else
+#define ahd_lastphase_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LASTPHASE", 0x136, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qoutfifo_entry_valid_tag_print;
+#else
+#define ahd_qoutfifo_entry_valid_tag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QOUTFIFO_ENTRY_VALID_TAG", 0x137, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_shared_data_addr_print;
+#else
+#define ahd_shared_data_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SHARED_DATA_ADDR", 0x138, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qoutfifo_next_addr_print;
+#else
+#define ahd_qoutfifo_next_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QOUTFIFO_NEXT_ADDR", 0x13c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_kernel_tqinpos_print;
+#else
+#define ahd_kernel_tqinpos_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "KERNEL_TQINPOS", 0x140, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_tqinpos_print;
+#else
+#define ahd_tqinpos_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TQINPOS", 0x141, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_arg_1_print;
+#else
+#define ahd_arg_1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ARG_1", 0x142, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_arg_2_print;
+#else
+#define ahd_arg_2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ARG_2", 0x143, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_last_msg_print;
+#else
+#define ahd_last_msg_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LAST_MSG", 0x144, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiseq_template_print;
+#else
+#define ahd_scsiseq_template_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISEQ_TEMPLATE", 0x145, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_initiator_tag_print;
+#else
+#define ahd_initiator_tag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INITIATOR_TAG", 0x146, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seq_flags2_print;
+#else
+#define ahd_seq_flags2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQ_FLAGS2", 0x147, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_allocfifo_scbptr_print;
+#else
+#define ahd_allocfifo_scbptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ALLOCFIFO_SCBPTR", 0x148, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalessing_timer_print;
+#else
+#define ahd_int_coalessing_timer_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INT_COALESSING_TIMER", 0x14a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalessing_maxcmds_print;
+#else
+#define ahd_int_coalessing_maxcmds_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INT_COALESSING_MAXCMDS", 0x14c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalessing_mincmds_print;
+#else
+#define ahd_int_coalessing_mincmds_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INT_COALESSING_MINCMDS", 0x14d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmds_pending_print;
+#else
+#define ahd_cmds_pending_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDS_PENDING", 0x14e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalessing_cmdcount_print;
+#else
+#define ahd_int_coalessing_cmdcount_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INT_COALESSING_CMDCOUNT", 0x150, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_local_hs_mailbox_print;
+#else
+#define ahd_local_hs_mailbox_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LOCAL_HS_MAILBOX", 0x151, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdsize_table_print;
+#else
+#define ahd_cmdsize_table_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDSIZE_TABLE", 0x152, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_base_print;
+#else
+#define ahd_scb_base_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_BASE", 0x180, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_residual_datacnt_print;
+#else
+#define ahd_scb_residual_datacnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_RESIDUAL_DATACNT", 0x180, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_residual_sgptr_print;
+#else
+#define ahd_scb_residual_sgptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_RESIDUAL_SGPTR", 0x184, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_scsi_status_print;
+#else
+#define ahd_scb_scsi_status_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SCSI_STATUS", 0x188, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_target_phases_print;
+#else
+#define ahd_scb_target_phases_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TARGET_PHASES", 0x189, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_target_data_dir_print;
+#else
+#define ahd_scb_target_data_dir_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TARGET_DATA_DIR", 0x18a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_target_itag_print;
+#else
+#define ahd_scb_target_itag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TARGET_ITAG", 0x18b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_sense_busaddr_print;
+#else
+#define ahd_scb_sense_busaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SENSE_BUSADDR", 0x18c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_tag_print;
+#else
+#define ahd_scb_tag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TAG", 0x190, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_cdb_len_print;
+#else
+#define ahd_scb_cdb_len_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_CDB_LEN", 0x192, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_task_management_print;
+#else
+#define ahd_scb_task_management_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TASK_MANAGEMENT", 0x193, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_next_print;
+#else
+#define ahd_scb_next_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_NEXT", 0x194, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_next2_print;
+#else
+#define ahd_scb_next2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_NEXT2", 0x196, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_dataptr_print;
+#else
+#define ahd_scb_dataptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_DATAPTR", 0x198, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_datacnt_print;
+#else
+#define ahd_scb_datacnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_DATACNT", 0x1a0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_sgptr_print;
+#else
+#define ahd_scb_sgptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SGPTR", 0x1a4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_control_print;
+#else
+#define ahd_scb_control_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_CONTROL", 0x1a8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_scsiid_print;
+#else
+#define ahd_scb_scsiid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SCSIID", 0x1a9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_lun_print;
+#else
+#define ahd_scb_lun_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_LUN", 0x1aa, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_task_attribute_print;
+#else
+#define ahd_scb_task_attribute_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TASK_ATTRIBUTE", 0x1ab, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_busaddr_print;
+#else
+#define ahd_scb_busaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_BUSADDR", 0x1ac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_spare_print;
+#else
+#define ahd_scb_spare_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SPARE", 0x1b0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_disconnected_lists_print;
+#else
+#define ahd_scb_disconnected_lists_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_DISCONNECTED_LISTS", 0x1b8, regvalue, cur_col, wrap)
+#endif
+
+
+#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 SAW_HWERR 0x17
+#define TRACEPOINT3 0x16
+#define TRACEPOINT2 0x15
+#define TRACEPOINT1 0x14
+#define TRACEPOINT0 0x13
+#define ENTERING_NONPACK 0x12
+#define CFG4OVERRUN 0x11
+#define STATUS_OVERRUN 0x10
+#define CFG4ISTAT_INTR 0x0f
+#define INVALID_SEQINT 0x0e
+#define ILLEGAL_PHASE 0x0d
+#define DUMP_CARD_STATE 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 NO_SEQINT 0x00
+
+#define CLRINT 0x03
+#define CLRHWERRINT 0x80
+#define CLRBRKADRINT 0x40
+#define CLRSWTMINT 0x20
+#define CLRPCIINT 0x10
+#define CLRSCSIINT 0x08
+#define CLRSEQINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSPLTINT 0x01
+
+#define ERROR 0x04
+#define CIOPARERR 0x80
+#define CIOACCESFAIL 0x40
+#define MPARERR 0x20
+#define DPARERR 0x10
+#define SQPARERR 0x08
+#define ILLOPCODE 0x04
+#define DSCTMOUT 0x02
+
+#define CLRERR 0x04
+#define CLRCIOPARERR 0x80
+#define CLRCIOACCESFAIL 0x40
+#define CLRMPARERR 0x20
+#define CLRDPARERR 0x10
+#define CLRSQPARERR 0x08
+#define CLRILLOPCODE 0x04
+#define CLRDSCTMOUT 0x02
+
+#define HCNTRL 0x05
+#define SEQ_RESET 0x80
+#define POWRDN 0x40
+#define SWINT 0x10
+#define SWTIMER_START_B 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 ENINT_COALESS 0x40
+
+#define CLRSEQINTSTAT 0x0c
+#define CLRSEQ_SWTMRTO 0x10
+#define CLRSEQ_SEQINT 0x08
+#define CLRSEQ_SCSIINT 0x04
+#define CLRSEQ_PCIINT 0x02
+#define CLRSEQ_SPLTINT 0x01
+
+#define SEQINTSTAT 0x0c
+#define SEQ_SWTMRTO 0x10
+#define SEQ_SEQINT 0x08
+#define SEQ_SCSIINT 0x04
+#define SEQ_PCIINT 0x02
+#define SEQ_SPLTINT 0x01
+
+#define SWTIMER 0x0e
+
+#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 SCSIENWRDIS 0x40
+#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 DISABLE_TWATE 0x02
+#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 MAXCMDBYTES 0x2d
+
+#define MAXCMD2RCV 0x2e
+
+#define SHORTTHRESH 0x2f
+
+#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 CURRFIFO 0x03
+#define FIFO1FREE 0x20
+#define FIFO0FREE 0x10
+#define CURRFIFO_NONE 0x03
+#define CURRFIFO_1 0x01
+#define CURRFIFO_0 0x00
+
+#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 OPTIONMODE_DEFAULTS 0x02
+#define BIOSCANCTL 0x80
+#define AUTOACKEN 0x40
+#define BIASCANCTL 0x20
+#define BUSFREEREV 0x10
+#define ENDGFORMCHK 0x04
+#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 BUSFREETIME 0xc0
+#define NONPACKREQ 0x20
+#define EXP_ACTIVE 0x10
+#define BSYX 0x08
+#define WIDE_RES 0x04
+#define SDONE 0x02
+#define DMADONE 0x01
+#define BUSFREE_DFF1 0xc0
+#define BUSFREE_DFF0 0x80
+#define BUSFREE_LQO 0x40
+
+#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 CLRLQIINT0 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 DFFBITBUCKET 0x08
+#define CLRSHCNT 0x04
+#define CLRCHN 0x02
+#define RSTCHN 0x01
+
+#define LQOSCSCTL 0x5a
+#define LQOH2A_VERSION 0x80
+#define LQONOCHKOVER 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 SHCNTNEGATIVE 0x40
+#define SHCNTMINUS1 0x20
+#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 ENSNAPSHOT 0x40
+#define RTI_WRTDIS 0x20
+#define RTI_OVRDTRN 0x10
+#define ENSLOWCRC 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNO 0x02
+#define WIDEXFER 0x01
+
+#define ANNEXCOL 0x65
+
+#define SCSCHKN 0x66
+#define STSELSKIDDIS 0x40
+#define CURRFIFODEF 0x20
+#define WIDERESEN 0x10
+#define SDONEMSKDIS 0x08
+#define DFFACTCLR 0x04
+#define SHVALIDSTDIS 0x02
+#define LSTSGCLRDIS 0x01
+
+#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 PLLDELAY 0x70
+#define SPLIT_DROP_REQ 0x80
+
+#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 RD_DFTHRSH 0x07
+#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_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 REG_ISR 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 0xa7
+#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 CCSGEN 0x0c
+#define CCSGDONE 0x80
+#define SG_CACHE_AVAIL 0x10
+#define CCSGENACK 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_EWEN 0x40
+#define SEEOP_WALL 0x40
+#define SEEOP_EWDS 0x40
+#define SEEOPCODE 0x70
+#define SEERST 0x02
+#define SEESTART 0x01
+#define SEEOP_ERASE 0x70
+#define SEEOP_READ 0x60
+#define SEEOP_WRITE 0x50
+#define SEEOP_ERAL 0x40
+
+#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 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 DFBKPTR 0xc9
+
+#define SKEWCALC 0xc9
+
+#define DFDBCTL 0xcb
+#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 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 INTMASK2 0x04
+#define INTMASK1 0x02
+#define IRET 0x01
+
+#define SEQRAM 0xda
+
+#define PRGMCNT 0xde
+
+#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 PHASE_MASK 0xe0
+#define CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
+#define P_BUSFREE 0x01
+#define P_MESGIN 0xe0
+#define P_STATUS 0xc0
+#define P_MESGOUT 0xa0
+#define P_COMMAND 0x80
+#define P_DATAIN_DT 0x60
+#define P_DATAIN 0x40
+#define P_DATAOUT_DT 0x20
+#define P_DATAOUT 0x00
+
+#define QOUTFIFO_ENTRY_VALID_TAG 0x137
+
+#define SHARED_DATA_ADDR 0x138
+
+#define QOUTFIFO_NEXT_ADDR 0x13c
+
+#define KERNEL_TQINPOS 0x140
+
+#define TQINPOS 0x141
+
+#define ARG_1 0x142
+#define RETURN_1 0x142
+#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 0x143
+#define RETURN_2 0x143
+
+#define LAST_MSG 0x144
+
+#define SCSISEQ_TEMPLATE 0x145
+#define MANUALCTL 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define MANUALP 0x0c
+#define ENAUTOATNP 0x02
+#define ALTSTIM 0x01
+
+#define INITIATOR_TAG 0x146
+
+#define SEQ_FLAGS2 0x147
+#define SELECTOUT_QFROZEN 0x04
+#define TARGET_MSG_PENDING 0x02
+
+#define ALLOCFIFO_SCBPTR 0x148
+
+#define INT_COALESSING_TIMER 0x14a
+
+#define INT_COALESSING_MAXCMDS 0x14c
+
+#define INT_COALESSING_MINCMDS 0x14d
+
+#define CMDS_PENDING 0x14e
+
+#define INT_COALESSING_CMDCOUNT 0x150
+
+#define LOCAL_HS_MAILBOX 0x151
+
+#define CMDSIZE_TABLE 0x152
+
+#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_TAG 0x190
+
+#define SCB_CDB_LEN 0x192
+#define SCB_CDB_LEN_PTR 0x80
+
+#define SCB_TASK_MANAGEMENT 0x193
+
+#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_BUSADDR 0x1ac
+
+#define SCB_SPARE 0x1b0
+#define SCB_PKT_LUN 0x1b0
+
+#define SCB_DISCONNECTED_LISTS 0x1b8
+
+
+#define AHD_TIMER_US_PER_TICK 0x19
+#define SCB_TRANSFER_SIZE_FULL_LUN 0x38
+#define STATUS_QUEUE_FULL 0x28
+#define STATUS_BUSY 0x08
+#define MAX_OFFSET_NON_PACED 0x7f
+#define MAX_OFFSET_PACED 0xfe
+#define BUS_32_BIT 0x02
+#define CCSGADDR_MAX 0x80
+#define TID_SHIFT 0x04
+#define MK_MESSAGE_BIT_OFFSET 0x04
+#define WRTBIASCTL_HP_DEFAULT 0x00
+#define SEEOP_EWDS_ADDR 0x00
+#define AHD_AMPLITUDE_SHIFT 0x00
+#define AHD_AMPLITUDE_MASK 0x07
+#define AHD_ANNEXCOL_AMPLITUDE 0x06
+#define AHD_SLEWRATE_DEF_REVA 0x01
+#define AHD_SLEWRATE_SHIFT 0x03
+#define AHD_SLEWRATE_MASK 0x78
+#define AHD_PRECOMP_CUTBACK_29 0x06
+#define AHD_NUM_PER_DEV_ANNEXCOLS 0x04
+#define B_CURRFIFO_0 0x02
+#define NVRAM_SCB_OFFSET 0x2c
+#define AHD_TIMER_MAX_US 0x18ffe7
+#define AHD_TIMER_MAX_TICKS 0xffff
+#define STATUS_PKT_SENSE 0xff
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define AHD_SENSE_BUFSIZE 0x100
+#define MAX_OFFSET_PACED_BUG 0x7f
+#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_AMPLITUDE_DEF 0x07
+#define AHD_SLEWRATE_DEF_REVB 0x08
+#define AHD_PRECOMP_CUTBACK_37 0x07
+#define AHD_PRECOMP_CUTBACK_17 0x04
+#define AHD_PRECOMP_SHIFT 0x00
+#define AHD_PRECOMP_MASK 0x07
+#define AHD_ANNEXCOL_PRECOMP_SLEW 0x04
+#define SRC_MODE_SHIFT 0x00
+#define PKT_OVERRUN_BUFSIZE 0x200
+#define SCB_TRANSFER_SIZE_1BYTE_LUN 0x30
+#define TARGET_DATA_IN 0x01
+#define HOST_MSG 0xff
+#define MAX_OFFSET 0xfe
+#define BUS_16_BIT 0x01
+#define CCSCBADDR_MAX 0x80
+#define NUMDSPS 0x14
+#define SEEOP_EWEN_ADDR 0xc0
+#define AHD_ANNEXCOL_PER_DEV0 0x04
+#define DST_MODE_SHIFT 0x04
+
+
+/* Downloaded Constant Definitions */
+#define SCB_TRANSFER_SIZE 0x06
+#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 0x07
+
+
+/* Exported Labels */
+#define LABEL_seq_isr 0x25a
+#define LABEL_timer_isr 0x256
diff --git a/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped b/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped
new file mode 100644
index 000000000000..3acb82b220bc
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped
@@ -0,0 +1,3630 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#77 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#59 $
+ */
+
+#include "aic79xx_osm.h"
+
+static ahd_reg_parse_entry_t MODE_PTR_parse_table[] = {
+ { "SRC_MODE", 0x07, 0x07 },
+ { "DST_MODE", 0x70, 0x70 }
+};
+
+int
+ahd_mode_ptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(MODE_PTR_parse_table, 2, "MODE_PTR",
+ 0x00, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t INTSTAT_parse_table[] = {
+ { "SPLTINT", 0x01, 0x01 },
+ { "CMDCMPLT", 0x02, 0x02 },
+ { "SEQINT", 0x04, 0x04 },
+ { "SCSIINT", 0x08, 0x08 },
+ { "PCIINT", 0x10, 0x10 },
+ { "SWTMINT", 0x20, 0x20 },
+ { "BRKADRINT", 0x40, 0x40 },
+ { "HWERRINT", 0x80, 0x80 },
+ { "INT_PEND", 0xff, 0xff }
+};
+
+int
+ahd_intstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(INTSTAT_parse_table, 9, "INTSTAT",
+ 0x01, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQINTCODE_parse_table[] = {
+ { "NO_SEQINT", 0x00, 0xff },
+ { "BAD_PHASE", 0x01, 0xff },
+ { "SEND_REJECT", 0x02, 0xff },
+ { "PROTO_VIOLATION", 0x03, 0xff },
+ { "NO_MATCH", 0x04, 0xff },
+ { "IGN_WIDE_RES", 0x05, 0xff },
+ { "PDATA_REINIT", 0x06, 0xff },
+ { "HOST_MSG_LOOP", 0x07, 0xff },
+ { "BAD_STATUS", 0x08, 0xff },
+ { "DATA_OVERRUN", 0x09, 0xff },
+ { "MKMSG_FAILED", 0x0a, 0xff },
+ { "MISSED_BUSFREE", 0x0b, 0xff },
+ { "DUMP_CARD_STATE", 0x0c, 0xff },
+ { "ILLEGAL_PHASE", 0x0d, 0xff },
+ { "INVALID_SEQINT", 0x0e, 0xff },
+ { "CFG4ISTAT_INTR", 0x0f, 0xff },
+ { "STATUS_OVERRUN", 0x10, 0xff },
+ { "CFG4OVERRUN", 0x11, 0xff },
+ { "ENTERING_NONPACK", 0x12, 0xff },
+ { "TRACEPOINT0", 0x13, 0xff },
+ { "TRACEPOINT1", 0x14, 0xff },
+ { "TRACEPOINT2", 0x15, 0xff },
+ { "TRACEPOINT3", 0x16, 0xff },
+ { "SAW_HWERR", 0x17, 0xff }
+};
+
+int
+ahd_seqintcode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQINTCODE_parse_table, 24, "SEQINTCODE",
+ 0x02, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRINT_parse_table[] = {
+ { "CLRSPLTINT", 0x01, 0x01 },
+ { "CLRCMDINT", 0x02, 0x02 },
+ { "CLRSEQINT", 0x04, 0x04 },
+ { "CLRSCSIINT", 0x08, 0x08 },
+ { "CLRPCIINT", 0x10, 0x10 },
+ { "CLRSWTMINT", 0x20, 0x20 },
+ { "CLRBRKADRINT", 0x40, 0x40 },
+ { "CLRHWERRINT", 0x80, 0x80 }
+};
+
+int
+ahd_clrint_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRINT_parse_table, 8, "CLRINT",
+ 0x03, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ERROR_parse_table[] = {
+ { "DSCTMOUT", 0x02, 0x02 },
+ { "ILLOPCODE", 0x04, 0x04 },
+ { "SQPARERR", 0x08, 0x08 },
+ { "DPARERR", 0x10, 0x10 },
+ { "MPARERR", 0x20, 0x20 },
+ { "CIOACCESFAIL", 0x40, 0x40 },
+ { "CIOPARERR", 0x80, 0x80 }
+};
+
+int
+ahd_error_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ERROR_parse_table, 7, "ERROR",
+ 0x04, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRERR_parse_table[] = {
+ { "CLRDSCTMOUT", 0x02, 0x02 },
+ { "CLRILLOPCODE", 0x04, 0x04 },
+ { "CLRSQPARERR", 0x08, 0x08 },
+ { "CLRDPARERR", 0x10, 0x10 },
+ { "CLRMPARERR", 0x20, 0x20 },
+ { "CLRCIOACCESFAIL", 0x40, 0x40 },
+ { "CLRCIOPARERR", 0x80, 0x80 }
+};
+
+int
+ahd_clrerr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRERR_parse_table, 7, "CLRERR",
+ 0x04, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t HCNTRL_parse_table[] = {
+ { "CHIPRST", 0x01, 0x01 },
+ { "CHIPRSTACK", 0x01, 0x01 },
+ { "INTEN", 0x02, 0x02 },
+ { "PAUSE", 0x04, 0x04 },
+ { "SWTIMER_START_B", 0x08, 0x08 },
+ { "SWINT", 0x10, 0x10 },
+ { "POWRDN", 0x40, 0x40 },
+ { "SEQ_RESET", 0x80, 0x80 }
+};
+
+int
+ahd_hcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(HCNTRL_parse_table, 8, "HCNTRL",
+ 0x05, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hnscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HNSCB_QOFF",
+ 0x06, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hescb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HESCB_QOFF",
+ 0x08, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t HS_MAILBOX_parse_table[] = {
+ { "ENINT_COALESS", 0x40, 0x40 },
+ { "HOST_TQINPOS", 0x80, 0x80 }
+};
+
+int
+ahd_hs_mailbox_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(HS_MAILBOX_parse_table, 2, "HS_MAILBOX",
+ 0x0b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSEQINTSTAT_parse_table[] = {
+ { "CLRSEQ_SPLTINT", 0x01, 0x01 },
+ { "CLRSEQ_PCIINT", 0x02, 0x02 },
+ { "CLRSEQ_SCSIINT", 0x04, 0x04 },
+ { "CLRSEQ_SEQINT", 0x08, 0x08 },
+ { "CLRSEQ_SWTMRTO", 0x10, 0x10 }
+};
+
+int
+ahd_clrseqintstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSEQINTSTAT_parse_table, 5, "CLRSEQINTSTAT",
+ 0x0c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQINTSTAT_parse_table[] = {
+ { "SEQ_SPLTINT", 0x01, 0x01 },
+ { "SEQ_PCIINT", 0x02, 0x02 },
+ { "SEQ_SCSIINT", 0x04, 0x04 },
+ { "SEQ_SEQINT", 0x08, 0x08 },
+ { "SEQ_SWTMRTO", 0x10, 0x10 }
+};
+
+int
+ahd_seqintstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQINTSTAT_parse_table, 5, "SEQINTSTAT",
+ 0x0c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_swtimer_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SWTIMER",
+ 0x0e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_snscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SNSCB_QOFF",
+ 0x10, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sescb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SESCB_QOFF",
+ 0x12, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sdscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SDSCB_QOFF",
+ 0x14, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t QOFF_CTLSTA_parse_table[] = {
+ { "SCB_QSIZE_4", 0x00, 0x0f },
+ { "SCB_QSIZE_8", 0x01, 0x0f },
+ { "SCB_QSIZE_16", 0x02, 0x0f },
+ { "SCB_QSIZE_32", 0x03, 0x0f },
+ { "SCB_QSIZE_64", 0x04, 0x0f },
+ { "SCB_QSIZE_128", 0x05, 0x0f },
+ { "SCB_QSIZE_256", 0x06, 0x0f },
+ { "SCB_QSIZE_512", 0x07, 0x0f },
+ { "SCB_QSIZE_1024", 0x08, 0x0f },
+ { "SCB_QSIZE_2048", 0x09, 0x0f },
+ { "SCB_QSIZE_4096", 0x0a, 0x0f },
+ { "SCB_QSIZE_8192", 0x0b, 0x0f },
+ { "SCB_QSIZE_16384", 0x0c, 0x0f },
+ { "SCB_QSIZE", 0x0f, 0x0f },
+ { "HS_MAILBOX_ACT", 0x10, 0x10 },
+ { "SDSCB_ROLLOVR", 0x20, 0x20 },
+ { "NEW_SCB_AVAIL", 0x40, 0x40 },
+ { "EMPTY_SCB_AVAIL", 0x80, 0x80 }
+};
+
+int
+ahd_qoff_ctlsta_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(QOFF_CTLSTA_parse_table, 18, "QOFF_CTLSTA",
+ 0x16, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t INTCTL_parse_table[] = {
+ { "SPLTINTEN", 0x01, 0x01 },
+ { "SEQINTEN", 0x02, 0x02 },
+ { "SCSIINTEN", 0x04, 0x04 },
+ { "PCIINTEN", 0x08, 0x08 },
+ { "AUTOCLRCMDINT", 0x10, 0x10 },
+ { "SWTIMER_START", 0x20, 0x20 },
+ { "SWTMINTEN", 0x40, 0x40 },
+ { "SWTMINTMASK", 0x80, 0x80 }
+};
+
+int
+ahd_intctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(INTCTL_parse_table, 8, "INTCTL",
+ 0x18, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFCNTRL_parse_table[] = {
+ { "DIRECTIONEN", 0x01, 0x01 },
+ { "FIFOFLUSH", 0x02, 0x02 },
+ { "FIFOFLUSHACK", 0x02, 0x02 },
+ { "DIRECTION", 0x04, 0x04 },
+ { "DIRECTIONACK", 0x04, 0x04 },
+ { "HDMAEN", 0x08, 0x08 },
+ { "HDMAENACK", 0x08, 0x08 },
+ { "SCSIEN", 0x20, 0x20 },
+ { "SCSIENACK", 0x20, 0x20 },
+ { "SCSIENWRDIS", 0x40, 0x40 },
+ { "PRELOADEN", 0x80, 0x80 }
+};
+
+int
+ahd_dfcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFCNTRL_parse_table, 11, "DFCNTRL",
+ 0x19, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSCOMMAND0_parse_table[] = {
+ { "CIOPARCKEN", 0x01, 0x01 },
+ { "DISABLE_TWATE", 0x02, 0x02 },
+ { "EXTREQLCK", 0x10, 0x10 },
+ { "MPARCKEN", 0x20, 0x20 },
+ { "DPARCKEN", 0x40, 0x40 },
+ { "CACHETHEN", 0x80, 0x80 }
+};
+
+int
+ahd_dscommand0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSCOMMAND0_parse_table, 6, "DSCOMMAND0",
+ 0x19, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFSTATUS_parse_table[] = {
+ { "FIFOEMP", 0x01, 0x01 },
+ { "FIFOFULL", 0x02, 0x02 },
+ { "DFTHRESH", 0x04, 0x04 },
+ { "HDONE", 0x08, 0x08 },
+ { "MREQPEND", 0x10, 0x10 },
+ { "PKT_PRELOAD_AVAIL", 0x40, 0x40 },
+ { "PRELOAD_AVAIL", 0x80, 0x80 }
+};
+
+int
+ahd_dfstatus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFSTATUS_parse_table, 7, "DFSTATUS",
+ 0x1a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SG_CACHE_SHADOW_parse_table[] = {
+ { "LAST_SEG_DONE", 0x01, 0x01 },
+ { "LAST_SEG", 0x02, 0x02 },
+ { "ODD_SEG", 0x04, 0x04 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahd_sg_cache_shadow_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SG_CACHE_SHADOW_parse_table, 4, "SG_CACHE_SHADOW",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ARBCTL_parse_table[] = {
+ { "USE_TIME", 0x07, 0x07 },
+ { "RETRY_SWEN", 0x08, 0x08 },
+ { "RESET_HARB", 0x80, 0x80 }
+};
+
+int
+ahd_arbctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ARBCTL_parse_table, 3, "ARBCTL",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SG_CACHE_PRE_parse_table[] = {
+ { "LAST_SEG", 0x02, 0x02 },
+ { "ODD_SEG", 0x04, 0x04 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahd_sg_cache_pre_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SG_CACHE_PRE_parse_table, 3, "SG_CACHE_PRE",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqin_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQIN",
+ 0x20, regvalue, cur_col, wrap));
+}
+
+int
+ahd_typeptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "TYPEPTR",
+ 0x20, regvalue, cur_col, wrap));
+}
+
+int
+ahd_tagptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "TAGPTR",
+ 0x21, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lunptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LUNPTR",
+ 0x22, regvalue, cur_col, wrap));
+}
+
+int
+ahd_datalenptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DATALENPTR",
+ 0x23, regvalue, cur_col, wrap));
+}
+
+int
+ahd_statlenptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "STATLENPTR",
+ 0x24, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmdlenptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDLENPTR",
+ 0x25, regvalue, cur_col, wrap));
+}
+
+int
+ahd_attrptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ATTRPTR",
+ 0x26, regvalue, cur_col, wrap));
+}
+
+int
+ahd_flagptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FLAGPTR",
+ 0x27, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmdptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDPTR",
+ 0x28, regvalue, cur_col, wrap));
+}
+
+int
+ahd_qnextptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "QNEXTPTR",
+ 0x29, regvalue, cur_col, wrap));
+}
+
+int
+ahd_idptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "IDPTR",
+ 0x2a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_abrtbyteptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ABRTBYTEPTR",
+ 0x2b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_abrtbitptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ABRTBITPTR",
+ 0x2c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_maxcmdbytes_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MAXCMDBYTES",
+ 0x2d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_maxcmd2rcv_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MAXCMD2RCV",
+ 0x2e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_shortthresh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SHORTTHRESH",
+ 0x2f, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lunlen_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LUNLEN",
+ 0x30, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cdblimit_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CDBLIMIT",
+ 0x31, regvalue, cur_col, wrap));
+}
+
+int
+ahd_maxcmd_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MAXCMD",
+ 0x32, regvalue, cur_col, wrap));
+}
+
+int
+ahd_maxcmdcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MAXCMDCNT",
+ 0x33, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqrsvd01_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQRSVD01",
+ 0x34, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqrsvd16_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQRSVD16",
+ 0x35, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqrsvd17_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQRSVD17",
+ 0x36, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmdrsvd0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDRSVD0",
+ 0x37, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQCTL0_parse_table[] = {
+ { "LQ0INITGCLT", 0x03, 0x03 },
+ { "LQ0TARGCLT", 0x0c, 0x0c },
+ { "LQIINITGCLT", 0x30, 0x30 },
+ { "LQITARGCLT", 0xc0, 0xc0 }
+};
+
+int
+ahd_lqctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQCTL0_parse_table, 4, "LQCTL0",
+ 0x38, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQCTL1_parse_table[] = {
+ { "ABORTPENDING", 0x01, 0x01 },
+ { "SINGLECMD", 0x02, 0x02 },
+ { "PCI2PCI", 0x04, 0x04 }
+};
+
+int
+ahd_lqctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQCTL1_parse_table, 3, "LQCTL1",
+ 0x38, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSBIST0_parse_table[] = {
+ { "OSBISTRUN", 0x01, 0x01 },
+ { "OSBISTDONE", 0x02, 0x02 },
+ { "OSBISTERR", 0x04, 0x04 },
+ { "GSBISTRUN", 0x10, 0x10 },
+ { "GSBISTDONE", 0x20, 0x20 },
+ { "GSBISTERR", 0x40, 0x40 }
+};
+
+int
+ahd_scsbist0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSBIST0_parse_table, 6, "SCSBIST0",
+ 0x39, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQCTL2_parse_table[] = {
+ { "LQOPAUSE", 0x01, 0x01 },
+ { "LQOTOIDLE", 0x02, 0x02 },
+ { "LQOCONTINUE", 0x04, 0x04 },
+ { "LQORETRY", 0x08, 0x08 },
+ { "LQIPAUSE", 0x10, 0x10 },
+ { "LQITOIDLE", 0x20, 0x20 },
+ { "LQICONTINUE", 0x40, 0x40 },
+ { "LQIRETRY", 0x80, 0x80 }
+};
+
+int
+ahd_lqctl2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQCTL2_parse_table, 8, "LQCTL2",
+ 0x39, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSBIST1_parse_table[] = {
+ { "NTBISTRUN", 0x01, 0x01 },
+ { "NTBISTDONE", 0x02, 0x02 },
+ { "NTBISTERR", 0x04, 0x04 }
+};
+
+int
+ahd_scsbist1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSBIST1_parse_table, 3, "SCSBIST1",
+ 0x3a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISEQ0_parse_table[] = {
+ { "SCSIRSTO", 0x01, 0x01 },
+ { "FORCEBUSFREE", 0x10, 0x10 },
+ { "ENARBO", 0x20, 0x20 },
+ { "ENSELO", 0x40, 0x40 },
+ { "TEMODEO", 0x80, 0x80 }
+};
+
+int
+ahd_scsiseq0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISEQ0_parse_table, 5, "SCSISEQ0",
+ 0x3a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISEQ1_parse_table[] = {
+ { "ALTSTIM", 0x01, 0x01 },
+ { "ENAUTOATNP", 0x02, 0x02 },
+ { "MANUALP", 0x0c, 0x0c },
+ { "ENRSELI", 0x10, 0x10 },
+ { "ENSELI", 0x20, 0x20 },
+ { "MANUALCTL", 0x40, 0x40 }
+};
+
+int
+ahd_scsiseq1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISEQ1_parse_table, 6, "SCSISEQ1",
+ 0x3b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SXFRCTL0_parse_table[] = {
+ { "SPIOEN", 0x08, 0x08 },
+ { "BIOSCANCELEN", 0x10, 0x10 },
+ { "DFPEXP", 0x40, 0x40 },
+ { "DFON", 0x80, 0x80 }
+};
+
+int
+ahd_sxfrctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SXFRCTL0_parse_table, 4, "SXFRCTL0",
+ 0x3c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_businitid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "BUSINITID",
+ 0x3c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dlcount_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DLCOUNT",
+ 0x3c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SXFRCTL1_parse_table[] = {
+ { "STPWEN", 0x01, 0x01 },
+ { "ACTNEGEN", 0x02, 0x02 },
+ { "ENSTIMER", 0x04, 0x04 },
+ { "STIMESEL", 0x18, 0x18 },
+ { "ENSPCHK", 0x20, 0x20 },
+ { "ENSACHK", 0x40, 0x40 },
+ { "BITBUCKET", 0x80, 0x80 }
+};
+
+int
+ahd_sxfrctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SXFRCTL1_parse_table, 7, "SXFRCTL1",
+ 0x3d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_bustargid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "BUSTARGID",
+ 0x3e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SXFRCTL2_parse_table[] = {
+ { "ASU", 0x07, 0x07 },
+ { "CMDDMAEN", 0x08, 0x08 },
+ { "AUTORSTDIS", 0x10, 0x10 }
+};
+
+int
+ahd_sxfrctl2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SXFRCTL2_parse_table, 3, "SXFRCTL2",
+ 0x3e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFFSTAT_parse_table[] = {
+ { "CURRFIFO_0", 0x00, 0x03 },
+ { "CURRFIFO_1", 0x01, 0x03 },
+ { "CURRFIFO_NONE", 0x03, 0x03 },
+ { "FIFO0FREE", 0x10, 0x10 },
+ { "FIFO1FREE", 0x20, 0x20 },
+ { "CURRFIFO", 0x03, 0x03 }
+};
+
+int
+ahd_dffstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFFSTAT_parse_table, 6, "DFFSTAT",
+ 0x3f, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISIGO_parse_table[] = {
+ { "P_DATAOUT", 0x00, 0xe0 },
+ { "P_DATAOUT_DT", 0x20, 0xe0 },
+ { "P_DATAIN", 0x40, 0xe0 },
+ { "P_DATAIN_DT", 0x60, 0xe0 },
+ { "P_COMMAND", 0x80, 0xe0 },
+ { "P_MESGOUT", 0xa0, 0xe0 },
+ { "P_STATUS", 0xc0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 },
+ { "ACKO", 0x01, 0x01 },
+ { "REQO", 0x02, 0x02 },
+ { "BSYO", 0x04, 0x04 },
+ { "SELO", 0x08, 0x08 },
+ { "ATNO", 0x10, 0x10 },
+ { "MSGO", 0x20, 0x20 },
+ { "IOO", 0x40, 0x40 },
+ { "CDO", 0x80, 0x80 },
+ { "PHASE_MASK", 0xe0, 0xe0 }
+};
+
+int
+ahd_scsisigo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISIGO_parse_table, 17, "SCSISIGO",
+ 0x40, regvalue, cur_col, wrap));
+}
+
+int
+ahd_multargid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MULTARGID",
+ 0x40, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISIGI_parse_table[] = {
+ { "P_DATAOUT", 0x00, 0xe0 },
+ { "P_DATAOUT_DT", 0x20, 0xe0 },
+ { "P_DATAIN", 0x40, 0xe0 },
+ { "P_DATAIN_DT", 0x60, 0xe0 },
+ { "P_COMMAND", 0x80, 0xe0 },
+ { "P_MESGOUT", 0xa0, 0xe0 },
+ { "P_STATUS", 0xc0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 },
+ { "ACKI", 0x01, 0x01 },
+ { "REQI", 0x02, 0x02 },
+ { "BSYI", 0x04, 0x04 },
+ { "SELI", 0x08, 0x08 },
+ { "ATNI", 0x10, 0x10 },
+ { "MSGI", 0x20, 0x20 },
+ { "IOI", 0x40, 0x40 },
+ { "CDI", 0x80, 0x80 },
+ { "PHASE_MASK", 0xe0, 0xe0 }
+};
+
+int
+ahd_scsisigi_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISIGI_parse_table, 17, "SCSISIGI",
+ 0x41, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSIPHASE_parse_table[] = {
+ { "DATA_OUT_PHASE", 0x01, 0x03 },
+ { "DATA_IN_PHASE", 0x02, 0x03 },
+ { "DATA_PHASE_MASK", 0x03, 0x03 },
+ { "MSG_OUT_PHASE", 0x04, 0x04 },
+ { "MSG_IN_PHASE", 0x08, 0x08 },
+ { "COMMAND_PHASE", 0x10, 0x10 },
+ { "STATUS_PHASE", 0x20, 0x20 }
+};
+
+int
+ahd_scsiphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSIPHASE_parse_table, 7, "SCSIPHASE",
+ 0x42, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scsidat0_img_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCSIDAT0_IMG",
+ 0x43, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scsidat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCSIDAT",
+ 0x44, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scsibus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCSIBUS",
+ 0x46, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t TARGIDIN_parse_table[] = {
+ { "TARGID", 0x0f, 0x0f },
+ { "CLKOUT", 0x80, 0x80 }
+};
+
+int
+ahd_targidin_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(TARGIDIN_parse_table, 2, "TARGIDIN",
+ 0x48, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SELID_parse_table[] = {
+ { "ONEBIT", 0x08, 0x08 },
+ { "SELID_MASK", 0xf0, 0xf0 }
+};
+
+int
+ahd_selid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SELID_parse_table, 2, "SELID",
+ 0x49, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SBLKCTL_parse_table[] = {
+ { "SELWIDE", 0x02, 0x02 },
+ { "ENAB20", 0x04, 0x04 },
+ { "ENAB40", 0x08, 0x08 },
+ { "DIAGLEDON", 0x40, 0x40 },
+ { "DIAGLEDEN", 0x80, 0x80 }
+};
+
+int
+ahd_sblkctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SBLKCTL_parse_table, 5, "SBLKCTL",
+ 0x4a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OPTIONMODE_parse_table[] = {
+ { "AUTO_MSGOUT_DE", 0x02, 0x02 },
+ { "ENDGFORMCHK", 0x04, 0x04 },
+ { "BUSFREEREV", 0x10, 0x10 },
+ { "BIASCANCTL", 0x20, 0x20 },
+ { "AUTOACKEN", 0x40, 0x40 },
+ { "BIOSCANCTL", 0x80, 0x80 },
+ { "OPTIONMODE_DEFAULTS",0x02, 0x02 }
+};
+
+int
+ahd_optionmode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OPTIONMODE_parse_table, 7, "OPTIONMODE",
+ 0x4a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SSTAT0_parse_table[] = {
+ { "ARBDO", 0x01, 0x01 },
+ { "SPIORDY", 0x02, 0x02 },
+ { "OVERRUN", 0x04, 0x04 },
+ { "IOERR", 0x08, 0x08 },
+ { "SELINGO", 0x10, 0x10 },
+ { "SELDI", 0x20, 0x20 },
+ { "SELDO", 0x40, 0x40 },
+ { "TARGET", 0x80, 0x80 }
+};
+
+int
+ahd_sstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SSTAT0_parse_table, 8, "SSTAT0",
+ 0x4b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSINT0_parse_table[] = {
+ { "CLRARBDO", 0x01, 0x01 },
+ { "CLRSPIORDY", 0x02, 0x02 },
+ { "CLROVERRUN", 0x04, 0x04 },
+ { "CLRIOERR", 0x08, 0x08 },
+ { "CLRSELINGO", 0x10, 0x10 },
+ { "CLRSELDI", 0x20, 0x20 },
+ { "CLRSELDO", 0x40, 0x40 }
+};
+
+int
+ahd_clrsint0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSINT0_parse_table, 7, "CLRSINT0",
+ 0x4b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SIMODE0_parse_table[] = {
+ { "ENARBDO", 0x01, 0x01 },
+ { "ENSPIORDY", 0x02, 0x02 },
+ { "ENOVERRUN", 0x04, 0x04 },
+ { "ENIOERR", 0x08, 0x08 },
+ { "ENSELINGO", 0x10, 0x10 },
+ { "ENSELDI", 0x20, 0x20 },
+ { "ENSELDO", 0x40, 0x40 }
+};
+
+int
+ahd_simode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SIMODE0_parse_table, 7, "SIMODE0",
+ 0x4b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSINT1_parse_table[] = {
+ { "CLRREQINIT", 0x01, 0x01 },
+ { "CLRSTRB2FAST", 0x02, 0x02 },
+ { "CLRSCSIPERR", 0x04, 0x04 },
+ { "CLRBUSFREE", 0x08, 0x08 },
+ { "CLRSCSIRSTI", 0x20, 0x20 },
+ { "CLRATNO", 0x40, 0x40 },
+ { "CLRSELTIMEO", 0x80, 0x80 }
+};
+
+int
+ahd_clrsint1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSINT1_parse_table, 7, "CLRSINT1",
+ 0x4c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SSTAT1_parse_table[] = {
+ { "REQINIT", 0x01, 0x01 },
+ { "STRB2FAST", 0x02, 0x02 },
+ { "SCSIPERR", 0x04, 0x04 },
+ { "BUSFREE", 0x08, 0x08 },
+ { "PHASEMIS", 0x10, 0x10 },
+ { "SCSIRSTI", 0x20, 0x20 },
+ { "ATNTARG", 0x40, 0x40 },
+ { "SELTO", 0x80, 0x80 }
+};
+
+int
+ahd_sstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SSTAT1_parse_table, 8, "SSTAT1",
+ 0x4c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SSTAT2_parse_table[] = {
+ { "BUSFREE_LQO", 0x40, 0xc0 },
+ { "BUSFREE_DFF0", 0x80, 0xc0 },
+ { "BUSFREE_DFF1", 0xc0, 0xc0 },
+ { "DMADONE", 0x01, 0x01 },
+ { "SDONE", 0x02, 0x02 },
+ { "WIDE_RES", 0x04, 0x04 },
+ { "BSYX", 0x08, 0x08 },
+ { "EXP_ACTIVE", 0x10, 0x10 },
+ { "NONPACKREQ", 0x20, 0x20 },
+ { "BUSFREETIME", 0xc0, 0xc0 }
+};
+
+int
+ahd_sstat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SSTAT2_parse_table, 10, "SSTAT2",
+ 0x4d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSINT2_parse_table[] = {
+ { "CLRDMADONE", 0x01, 0x01 },
+ { "CLRSDONE", 0x02, 0x02 },
+ { "CLRWIDE_RES", 0x04, 0x04 },
+ { "CLRNONPACKREQ", 0x20, 0x20 }
+};
+
+int
+ahd_clrsint2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSINT2_parse_table, 4, "CLRSINT2",
+ 0x4d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SIMODE2_parse_table[] = {
+ { "ENDMADONE", 0x01, 0x01 },
+ { "ENSDONE", 0x02, 0x02 },
+ { "ENWIDE_RES", 0x04, 0x04 }
+};
+
+int
+ahd_simode2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SIMODE2_parse_table, 3, "SIMODE2",
+ 0x4d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PERRDIAG_parse_table[] = {
+ { "DTERR", 0x01, 0x01 },
+ { "DGFORMERR", 0x02, 0x02 },
+ { "CRCERR", 0x04, 0x04 },
+ { "AIPERR", 0x08, 0x08 },
+ { "PARITYERR", 0x10, 0x10 },
+ { "PREVPHASE", 0x20, 0x20 },
+ { "HIPERR", 0x40, 0x40 },
+ { "HIZERO", 0x80, 0x80 }
+};
+
+int
+ahd_perrdiag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PERRDIAG_parse_table, 8, "PERRDIAG",
+ 0x4e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqistate_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQISTATE",
+ 0x4e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_soffcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SOFFCNT",
+ 0x4f, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqostate_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQOSTATE",
+ 0x4f, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQISTAT0_parse_table[] = {
+ { "LQIATNCMD", 0x01, 0x01 },
+ { "LQIATNLQ", 0x02, 0x02 },
+ { "LQIBADLQT", 0x04, 0x04 },
+ { "LQICRCT2", 0x08, 0x08 },
+ { "LQICRCT1", 0x10, 0x10 },
+ { "LQIATNQAS", 0x20, 0x20 }
+};
+
+int
+ahd_lqistat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQISTAT0_parse_table, 6, "LQISTAT0",
+ 0x50, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRLQIINT0_parse_table[] = {
+ { "CLRLQIATNCMD", 0x01, 0x01 },
+ { "CLRLQIATNLQ", 0x02, 0x02 },
+ { "CLRLQIBADLQT", 0x04, 0x04 },
+ { "CLRLQICRCT2", 0x08, 0x08 },
+ { "CLRLQICRCT1", 0x10, 0x10 },
+ { "CLRLQIATNQAS", 0x20, 0x20 }
+};
+
+int
+ahd_clrlqiint0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRLQIINT0_parse_table, 6, "CLRLQIINT0",
+ 0x50, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQIMODE0_parse_table[] = {
+ { "ENLQIATNCMD", 0x01, 0x01 },
+ { "ENLQIATNLQ", 0x02, 0x02 },
+ { "ENLQIBADLQT", 0x04, 0x04 },
+ { "ENLQICRCT2", 0x08, 0x08 },
+ { "ENLQICRCT1", 0x10, 0x10 },
+ { "ENLQIATNQASK", 0x20, 0x20 }
+};
+
+int
+ahd_lqimode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQIMODE0_parse_table, 6, "LQIMODE0",
+ 0x50, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQIMODE1_parse_table[] = {
+ { "ENLQIOVERI_NLQ", 0x01, 0x01 },
+ { "ENLQIOVERI_LQ", 0x02, 0x02 },
+ { "ENLQIBADLQI", 0x04, 0x04 },
+ { "ENLQICRCI_NLQ", 0x08, 0x08 },
+ { "ENLQICRCI_LQ", 0x10, 0x10 },
+ { "ENLIQABORT", 0x20, 0x20 },
+ { "ENLQIPHASE_NLQ", 0x40, 0x40 },
+ { "ENLQIPHASE_LQ", 0x80, 0x80 }
+};
+
+int
+ahd_lqimode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQIMODE1_parse_table, 8, "LQIMODE1",
+ 0x51, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQISTAT1_parse_table[] = {
+ { "LQIOVERI_NLQ", 0x01, 0x01 },
+ { "LQIOVERI_LQ", 0x02, 0x02 },
+ { "LQIBADLQI", 0x04, 0x04 },
+ { "LQICRCI_NLQ", 0x08, 0x08 },
+ { "LQICRCI_LQ", 0x10, 0x10 },
+ { "LQIABORT", 0x20, 0x20 },
+ { "LQIPHASE_NLQ", 0x40, 0x40 },
+ { "LQIPHASE_LQ", 0x80, 0x80 }
+};
+
+int
+ahd_lqistat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQISTAT1_parse_table, 8, "LQISTAT1",
+ 0x51, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRLQIINT1_parse_table[] = {
+ { "CLRLQIOVERI_NLQ", 0x01, 0x01 },
+ { "CLRLQIOVERI_LQ", 0x02, 0x02 },
+ { "CLRLQIBADLQI", 0x04, 0x04 },
+ { "CLRLQICRCI_NLQ", 0x08, 0x08 },
+ { "CLRLQICRCI_LQ", 0x10, 0x10 },
+ { "CLRLIQABORT", 0x20, 0x20 },
+ { "CLRLQIPHASE_NLQ", 0x40, 0x40 },
+ { "CLRLQIPHASE_LQ", 0x80, 0x80 }
+};
+
+int
+ahd_clrlqiint1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRLQIINT1_parse_table, 8, "CLRLQIINT1",
+ 0x51, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQISTAT2_parse_table[] = {
+ { "LQIGSAVAIL", 0x01, 0x01 },
+ { "LQISTOPCMD", 0x02, 0x02 },
+ { "LQISTOPLQ", 0x04, 0x04 },
+ { "LQISTOPPKT", 0x08, 0x08 },
+ { "LQIWAITFIFO", 0x10, 0x10 },
+ { "LQIWORKONLQ", 0x20, 0x20 },
+ { "LQIPHASE_OUTPKT", 0x40, 0x40 },
+ { "PACKETIZED", 0x80, 0x80 }
+};
+
+int
+ahd_lqistat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQISTAT2_parse_table, 8, "LQISTAT2",
+ 0x52, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SSTAT3_parse_table[] = {
+ { "OSRAMPERR", 0x01, 0x01 },
+ { "NTRAMPERR", 0x02, 0x02 }
+};
+
+int
+ahd_sstat3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SSTAT3_parse_table, 2, "SSTAT3",
+ 0x53, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SIMODE3_parse_table[] = {
+ { "ENOSRAMPERR", 0x01, 0x01 },
+ { "ENNTRAMPERR", 0x02, 0x02 }
+};
+
+int
+ahd_simode3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SIMODE3_parse_table, 2, "SIMODE3",
+ 0x53, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSINT3_parse_table[] = {
+ { "CLROSRAMPERR", 0x01, 0x01 },
+ { "CLRNTRAMPERR", 0x02, 0x02 }
+};
+
+int
+ahd_clrsint3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSINT3_parse_table, 2, "CLRSINT3",
+ 0x53, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOMODE0_parse_table[] = {
+ { "ENLQOTCRC", 0x01, 0x01 },
+ { "ENLQOATNPKT", 0x02, 0x02 },
+ { "ENLQOATNLQ", 0x04, 0x04 },
+ { "ENLQOSTOPT2", 0x08, 0x08 },
+ { "ENLQOTARGSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_lqomode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOMODE0_parse_table, 5, "LQOMODE0",
+ 0x54, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOSTAT0_parse_table[] = {
+ { "LQOTCRC", 0x01, 0x01 },
+ { "LQOATNPKT", 0x02, 0x02 },
+ { "LQOATNLQ", 0x04, 0x04 },
+ { "LQOSTOPT2", 0x08, 0x08 },
+ { "LQOTARGSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_lqostat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOSTAT0_parse_table, 5, "LQOSTAT0",
+ 0x54, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRLQOINT0_parse_table[] = {
+ { "CLRLQOTCRC", 0x01, 0x01 },
+ { "CLRLQOATNPKT", 0x02, 0x02 },
+ { "CLRLQOATNLQ", 0x04, 0x04 },
+ { "CLRLQOSTOPT2", 0x08, 0x08 },
+ { "CLRLQOTARGSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_clrlqoint0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRLQOINT0_parse_table, 5, "CLRLQOINT0",
+ 0x54, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOSTAT1_parse_table[] = {
+ { "LQOPHACHGINPKT", 0x01, 0x01 },
+ { "LQOBUSFREE", 0x02, 0x02 },
+ { "LQOBADQAS", 0x04, 0x04 },
+ { "LQOSTOPI2", 0x08, 0x08 },
+ { "LQOINITSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_lqostat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOSTAT1_parse_table, 5, "LQOSTAT1",
+ 0x55, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRLQOINT1_parse_table[] = {
+ { "CLRLQOPHACHGINPKT", 0x01, 0x01 },
+ { "CLRLQOBUSFREE", 0x02, 0x02 },
+ { "CLRLQOBADQAS", 0x04, 0x04 },
+ { "CLRLQOSTOPI2", 0x08, 0x08 },
+ { "CLRLQOINITSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_clrlqoint1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRLQOINT1_parse_table, 5, "CLRLQOINT1",
+ 0x55, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOMODE1_parse_table[] = {
+ { "ENLQOPHACHGINPKT", 0x01, 0x01 },
+ { "ENLQOBUSFREE", 0x02, 0x02 },
+ { "ENLQOBADQAS", 0x04, 0x04 },
+ { "ENLQOSTOPI2", 0x08, 0x08 },
+ { "ENLQOINITSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_lqomode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOMODE1_parse_table, 5, "LQOMODE1",
+ 0x55, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOSTAT2_parse_table[] = {
+ { "LQOSTOP0", 0x01, 0x01 },
+ { "LQOPHACHGOUTPKT", 0x02, 0x02 },
+ { "LQOWAITFIFO", 0x10, 0x10 },
+ { "LQOPKT", 0xe0, 0xe0 }
+};
+
+int
+ahd_lqostat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOSTAT2_parse_table, 4, "LQOSTAT2",
+ 0x56, regvalue, cur_col, wrap));
+}
+
+int
+ahd_os_space_cnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "OS_SPACE_CNT",
+ 0x56, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SIMODE1_parse_table[] = {
+ { "ENREQINIT", 0x01, 0x01 },
+ { "ENSTRB2FAST", 0x02, 0x02 },
+ { "ENSCSIPERR", 0x04, 0x04 },
+ { "ENBUSFREE", 0x08, 0x08 },
+ { "ENPHASEMIS", 0x10, 0x10 },
+ { "ENSCSIRST", 0x20, 0x20 },
+ { "ENATNTARG", 0x40, 0x40 },
+ { "ENSELTIMO", 0x80, 0x80 }
+};
+
+int
+ahd_simode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SIMODE1_parse_table, 8, "SIMODE1",
+ 0x57, regvalue, cur_col, wrap));
+}
+
+int
+ahd_gsfifo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "GSFIFO",
+ 0x58, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFFSXFRCTL_parse_table[] = {
+ { "RSTCHN", 0x01, 0x01 },
+ { "CLRCHN", 0x02, 0x02 },
+ { "CLRSHCNT", 0x04, 0x04 },
+ { "DFFBITBUCKET", 0x08, 0x08 }
+};
+
+int
+ahd_dffsxfrctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFFSXFRCTL_parse_table, 4, "DFFSXFRCTL",
+ 0x5a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOSCSCTL_parse_table[] = {
+ { "LQONOCHKOVER", 0x01, 0x01 },
+ { "LQOH2A_VERSION", 0x80, 0x80 }
+};
+
+int
+ahd_lqoscsctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOSCSCTL_parse_table, 2, "LQOSCSCTL",
+ 0x5a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_nextscb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEXTSCB",
+ 0x5a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSEQINTSRC_parse_table[] = {
+ { "CLRCFG4TCMD", 0x01, 0x01 },
+ { "CLRCFG4ICMD", 0x02, 0x02 },
+ { "CLRCFG4TSTAT", 0x04, 0x04 },
+ { "CLRCFG4ISTAT", 0x08, 0x08 },
+ { "CLRCFG4DATA", 0x10, 0x10 },
+ { "CLRSAVEPTRS", 0x20, 0x20 },
+ { "CLRCTXTDONE", 0x40, 0x40 }
+};
+
+int
+ahd_clrseqintsrc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSEQINTSRC_parse_table, 7, "CLRSEQINTSRC",
+ 0x5b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQINTSRC_parse_table[] = {
+ { "CFG4TCMD", 0x01, 0x01 },
+ { "CFG4ICMD", 0x02, 0x02 },
+ { "CFG4TSTAT", 0x04, 0x04 },
+ { "CFG4ISTAT", 0x08, 0x08 },
+ { "CFG4DATA", 0x10, 0x10 },
+ { "SAVEPTRS", 0x20, 0x20 },
+ { "CTXTDONE", 0x40, 0x40 }
+};
+
+int
+ahd_seqintsrc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQINTSRC_parse_table, 7, "SEQINTSRC",
+ 0x5b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_currscb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CURRSCB",
+ 0x5c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQIMODE_parse_table[] = {
+ { "ENCFG4TCMD", 0x01, 0x01 },
+ { "ENCFG4ICMD", 0x02, 0x02 },
+ { "ENCFG4TSTAT", 0x04, 0x04 },
+ { "ENCFG4ISTAT", 0x08, 0x08 },
+ { "ENCFG4DATA", 0x10, 0x10 },
+ { "ENSAVEPTRS", 0x20, 0x20 },
+ { "ENCTXTDONE", 0x40, 0x40 }
+};
+
+int
+ahd_seqimode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQIMODE_parse_table, 7, "SEQIMODE",
+ 0x5c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t MDFFSTAT_parse_table[] = {
+ { "FIFOFREE", 0x01, 0x01 },
+ { "DATAINFIFO", 0x02, 0x02 },
+ { "DLZERO", 0x04, 0x04 },
+ { "SHVALID", 0x08, 0x08 },
+ { "LASTSDONE", 0x10, 0x10 },
+ { "SHCNTMINUS1", 0x20, 0x20 },
+ { "SHCNTNEGATIVE", 0x40, 0x40 }
+};
+
+int
+ahd_mdffstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(MDFFSTAT_parse_table, 7, "MDFFSTAT",
+ 0x5d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CRCCONTROL_parse_table[] = {
+ { "CRCVALCHKEN", 0x40, 0x40 }
+};
+
+int
+ahd_crccontrol_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CRCCONTROL_parse_table, 1, "CRCCONTROL",
+ 0x5d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfftag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFFTAG",
+ 0x5e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lastscb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LASTSCB",
+ 0x5e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSITEST_parse_table[] = {
+ { "SEL_TXPLL_DEBUG", 0x04, 0x04 },
+ { "CNTRTEST", 0x08, 0x08 }
+};
+
+int
+ahd_scsitest_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSITEST_parse_table, 2, "SCSITEST",
+ 0x5e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t IOPDNCTL_parse_table[] = {
+ { "PDN_DIFFSENSE", 0x01, 0x01 },
+ { "PDN_IDIST", 0x04, 0x04 },
+ { "DISABLE_OE", 0x80, 0x80 }
+};
+
+int
+ahd_iopdnctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(IOPDNCTL_parse_table, 3, "IOPDNCTL",
+ 0x5f, regvalue, cur_col, wrap));
+}
+
+int
+ahd_shaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SHADDR",
+ 0x60, regvalue, cur_col, wrap));
+}
+
+int
+ahd_negoaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEGOADDR",
+ 0x60, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dgrpcrci_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DGRPCRCI",
+ 0x60, regvalue, cur_col, wrap));
+}
+
+int
+ahd_negperiod_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEGPERIOD",
+ 0x61, regvalue, cur_col, wrap));
+}
+
+int
+ahd_packcrci_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "PACKCRCI",
+ 0x62, regvalue, cur_col, wrap));
+}
+
+int
+ahd_negoffset_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEGOFFSET",
+ 0x62, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t NEGPPROPTS_parse_table[] = {
+ { "PPROPT_IUT", 0x01, 0x01 },
+ { "PPROPT_DT", 0x02, 0x02 },
+ { "PPROPT_QAS", 0x04, 0x04 },
+ { "PPROPT_PACE", 0x08, 0x08 }
+};
+
+int
+ahd_negppropts_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NEGPPROPTS_parse_table, 4, "NEGPPROPTS",
+ 0x63, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t NEGCONOPTS_parse_table[] = {
+ { "WIDEXFER", 0x01, 0x01 },
+ { "ENAUTOATNO", 0x02, 0x02 },
+ { "ENAUTOATNI", 0x04, 0x04 },
+ { "ENSLOWCRC", 0x08, 0x08 },
+ { "RTI_OVRDTRN", 0x10, 0x10 },
+ { "RTI_WRTDIS", 0x20, 0x20 },
+ { "ENSNAPSHOT", 0x40, 0x40 }
+};
+
+int
+ahd_negconopts_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NEGCONOPTS_parse_table, 7, "NEGCONOPTS",
+ 0x64, regvalue, cur_col, wrap));
+}
+
+int
+ahd_annexcol_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ANNEXCOL",
+ 0x65, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSCHKN_parse_table[] = {
+ { "LSTSGCLRDIS", 0x01, 0x01 },
+ { "SHVALIDSTDIS", 0x02, 0x02 },
+ { "DFFACTCLR", 0x04, 0x04 },
+ { "SDONEMSKDIS", 0x08, 0x08 },
+ { "WIDERESEN", 0x10, 0x10 },
+ { "CURRFIFODEF", 0x20, 0x20 },
+ { "STSELSKIDDIS", 0x40, 0x40 }
+};
+
+int
+ahd_scschkn_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSCHKN_parse_table, 7, "SCSCHKN",
+ 0x66, regvalue, cur_col, wrap));
+}
+
+int
+ahd_annexdat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ANNEXDAT",
+ 0x66, regvalue, cur_col, wrap));
+}
+
+int
+ahd_iownid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "IOWNID",
+ 0x67, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLL960CTL0_parse_table[] = {
+ { "PLL_ENFBM", 0x01, 0x01 },
+ { "PLL_DLPF", 0x02, 0x02 },
+ { "PLL_ENLPF", 0x04, 0x04 },
+ { "PLL_ENLUD", 0x08, 0x08 },
+ { "PLL_NS", 0x30, 0x30 },
+ { "PLL_PWDN", 0x40, 0x40 },
+ { "PLL_VCOSEL", 0x80, 0x80 }
+};
+
+int
+ahd_pll960ctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLL960CTL0_parse_table, 7, "PLL960CTL0",
+ 0x68, regvalue, cur_col, wrap));
+}
+
+int
+ahd_shcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SHCNT",
+ 0x68, regvalue, cur_col, wrap));
+}
+
+int
+ahd_townid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "TOWNID",
+ 0x69, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLL960CTL1_parse_table[] = {
+ { "PLL_RST", 0x01, 0x01 },
+ { "PLL_CNTCLR", 0x40, 0x40 },
+ { "PLL_CNTEN", 0x80, 0x80 }
+};
+
+int
+ahd_pll960ctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLL960CTL1_parse_table, 3, "PLL960CTL1",
+ 0x69, regvalue, cur_col, wrap));
+}
+
+int
+ahd_pll960cnt0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "PLL960CNT0",
+ 0x6a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_xsig_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "XSIG",
+ 0x6a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_seloid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SELOID",
+ 0x6b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLL400CTL0_parse_table[] = {
+ { "PLL_ENFBM", 0x01, 0x01 },
+ { "PLL_DLPF", 0x02, 0x02 },
+ { "PLL_ENLPF", 0x04, 0x04 },
+ { "PLL_ENLUD", 0x08, 0x08 },
+ { "PLL_NS", 0x30, 0x30 },
+ { "PLL_PWDN", 0x40, 0x40 },
+ { "PLL_VCOSEL", 0x80, 0x80 }
+};
+
+int
+ahd_pll400ctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLL400CTL0_parse_table, 7, "PLL400CTL0",
+ 0x6c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_fairness_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FAIRNESS",
+ 0x6c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLL400CTL1_parse_table[] = {
+ { "PLL_RST", 0x01, 0x01 },
+ { "PLL_CNTCLR", 0x40, 0x40 },
+ { "PLL_CNTEN", 0x80, 0x80 }
+};
+
+int
+ahd_pll400ctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLL400CTL1_parse_table, 3, "PLL400CTL1",
+ 0x6d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_pll400cnt0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "PLL400CNT0",
+ 0x6e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_unfairness_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "UNFAIRNESS",
+ 0x6e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_haddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HADDR",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLLDELAY_parse_table[] = {
+ { "SPLIT_DROP_REQ", 0x80, 0x80 }
+};
+
+int
+ahd_plldelay_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLLDELAY_parse_table, 1, "PLLDELAY",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hodmaadr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HODMAADR",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hodmacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HODMACNT",
+ 0x78, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HCNT",
+ 0x78, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hodmaen_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HODMAEN",
+ 0x7a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sghaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SGHADDR",
+ 0x7c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scbhaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCBHADDR",
+ 0x7c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sghcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SGHCNT",
+ 0x84, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scbhcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCBHCNT",
+ 0x84, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFF_THRSH_parse_table[] = {
+ { "WR_DFTHRSH_MIN", 0x00, 0x70 },
+ { "RD_DFTHRSH_MIN", 0x00, 0x07 },
+ { "RD_DFTHRSH_25", 0x01, 0x07 },
+ { "RD_DFTHRSH_50", 0x02, 0x07 },
+ { "RD_DFTHRSH_63", 0x03, 0x07 },
+ { "RD_DFTHRSH_75", 0x04, 0x07 },
+ { "RD_DFTHRSH_85", 0x05, 0x07 },
+ { "RD_DFTHRSH_90", 0x06, 0x07 },
+ { "RD_DFTHRSH_MAX", 0x07, 0x07 },
+ { "WR_DFTHRSH_25", 0x10, 0x70 },
+ { "WR_DFTHRSH_50", 0x20, 0x70 },
+ { "WR_DFTHRSH_63", 0x30, 0x70 },
+ { "WR_DFTHRSH_75", 0x40, 0x70 },
+ { "WR_DFTHRSH_85", 0x50, 0x70 },
+ { "WR_DFTHRSH_90", 0x60, 0x70 },
+ { "WR_DFTHRSH_MAX", 0x70, 0x70 },
+ { "RD_DFTHRSH", 0x07, 0x07 },
+ { "WR_DFTHRSH", 0x70, 0x70 }
+};
+
+int
+ahd_dff_thrsh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFF_THRSH_parse_table, 18, "DFF_THRSH",
+ 0x88, regvalue, cur_col, wrap));
+}
+
+int
+ahd_romaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ROMADDR",
+ 0x8a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ROMCNTRL_parse_table[] = {
+ { "RDY", 0x01, 0x01 },
+ { "REPEAT", 0x02, 0x02 },
+ { "ROMSPD", 0x18, 0x18 },
+ { "ROMOP", 0xe0, 0xe0 }
+};
+
+int
+ahd_romcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ROMCNTRL_parse_table, 4, "ROMCNTRL",
+ 0x8d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_romdata_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ROMDATA",
+ 0x8e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCRXMSG0_parse_table[] = {
+ { "CFNUM", 0x07, 0x07 },
+ { "CDNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_cmcrxmsg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCRXMSG0_parse_table, 2, "CMCRXMSG0",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ROENABLE_parse_table[] = {
+ { "DCH0ROEN", 0x01, 0x01 },
+ { "DCH1ROEN", 0x02, 0x02 },
+ { "SGROEN", 0x04, 0x04 },
+ { "CMCROEN", 0x08, 0x08 },
+ { "OVLYROEN", 0x10, 0x10 },
+ { "MSIROEN", 0x20, 0x20 }
+};
+
+int
+ahd_roenable_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ROENABLE_parse_table, 6, "ROENABLE",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYRXMSG0_parse_table[] = {
+ { "CFNUM", 0x07, 0x07 },
+ { "CDNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_ovlyrxmsg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYRXMSG0_parse_table, 2, "OVLYRXMSG0",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHRXMSG0_parse_table[] = {
+ { "CFNUM", 0x07, 0x07 },
+ { "CDNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_dchrxmsg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHRXMSG0_parse_table, 2, "DCHRXMSG0",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYRXMSG1_parse_table[] = {
+ { "CBNUM", 0xff, 0xff }
+};
+
+int
+ahd_ovlyrxmsg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYRXMSG1_parse_table, 1, "OVLYRXMSG1",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t NSENABLE_parse_table[] = {
+ { "DCH0NSEN", 0x01, 0x01 },
+ { "DCH1NSEN", 0x02, 0x02 },
+ { "SGNSEN", 0x04, 0x04 },
+ { "CMCNSEN", 0x08, 0x08 },
+ { "OVLYNSEN", 0x10, 0x10 },
+ { "MSINSEN", 0x20, 0x20 }
+};
+
+int
+ahd_nsenable_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NSENABLE_parse_table, 6, "NSENABLE",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHRXMSG1_parse_table[] = {
+ { "CBNUM", 0xff, 0xff }
+};
+
+int
+ahd_dchrxmsg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHRXMSG1_parse_table, 1, "DCHRXMSG1",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCRXMSG1_parse_table[] = {
+ { "CBNUM", 0xff, 0xff }
+};
+
+int
+ahd_cmcrxmsg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCRXMSG1_parse_table, 1, "CMCRXMSG1",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHRXMSG2_parse_table[] = {
+ { "MINDEX", 0xff, 0xff }
+};
+
+int
+ahd_dchrxmsg2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHRXMSG2_parse_table, 1, "DCHRXMSG2",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYRXMSG2_parse_table[] = {
+ { "MINDEX", 0xff, 0xff }
+};
+
+int
+ahd_ovlyrxmsg2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYRXMSG2_parse_table, 1, "OVLYRXMSG2",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCRXMSG2_parse_table[] = {
+ { "MINDEX", 0xff, 0xff }
+};
+
+int
+ahd_cmcrxmsg2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCRXMSG2_parse_table, 1, "CMCRXMSG2",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ost_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "OST",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHRXMSG3_parse_table[] = {
+ { "MCLASS", 0x0f, 0x0f }
+};
+
+int
+ahd_dchrxmsg3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHRXMSG3_parse_table, 1, "DCHRXMSG3",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCRXMSG3_parse_table[] = {
+ { "MCLASS", 0x0f, 0x0f }
+};
+
+int
+ahd_cmcrxmsg3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCRXMSG3_parse_table, 1, "CMCRXMSG3",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PCIXCTL_parse_table[] = {
+ { "CMPABCDIS", 0x01, 0x01 },
+ { "TSCSERREN", 0x02, 0x02 },
+ { "SRSPDPEEN", 0x04, 0x04 },
+ { "SPLTSTADIS", 0x08, 0x08 },
+ { "SPLTSMADIS", 0x10, 0x10 },
+ { "UNEXPSCIEN", 0x20, 0x20 },
+ { "SERRPULSE", 0x80, 0x80 }
+};
+
+int
+ahd_pcixctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PCIXCTL_parse_table, 7, "PCIXCTL",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYRXMSG3_parse_table[] = {
+ { "MCLASS", 0x0f, 0x0f }
+};
+
+int
+ahd_ovlyrxmsg3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYRXMSG3_parse_table, 1, "OVLYRXMSG3",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ovlyseqbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "OVLYSEQBCNT",
+ 0x94, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmcseqbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMCSEQBCNT",
+ 0x94, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dchseqbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DCHSEQBCNT",
+ 0x94, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCSPLTSTAT0_parse_table[] = {
+ { "RXSPLTRSP", 0x01, 0x01 },
+ { "RXSCEMSG", 0x02, 0x02 },
+ { "RXOVRUN", 0x04, 0x04 },
+ { "CNTNOTCMPLT", 0x08, 0x08 },
+ { "SCDATBUCKET", 0x10, 0x10 },
+ { "SCADERR", 0x20, 0x20 },
+ { "SCBCERR", 0x40, 0x40 },
+ { "STAETERM", 0x80, 0x80 }
+};
+
+int
+ahd_cmcspltstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCSPLTSTAT0_parse_table, 8, "CMCSPLTSTAT0",
+ 0x96, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYSPLTSTAT0_parse_table[] = {
+ { "RXSPLTRSP", 0x01, 0x01 },
+ { "RXSCEMSG", 0x02, 0x02 },
+ { "RXOVRUN", 0x04, 0x04 },
+ { "CNTNOTCMPLT", 0x08, 0x08 },
+ { "SCDATBUCKET", 0x10, 0x10 },
+ { "SCADERR", 0x20, 0x20 },
+ { "SCBCERR", 0x40, 0x40 },
+ { "STAETERM", 0x80, 0x80 }
+};
+
+int
+ahd_ovlyspltstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYSPLTSTAT0_parse_table, 8, "OVLYSPLTSTAT0",
+ 0x96, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHSPLTSTAT0_parse_table[] = {
+ { "RXSPLTRSP", 0x01, 0x01 },
+ { "RXSCEMSG", 0x02, 0x02 },
+ { "RXOVRUN", 0x04, 0x04 },
+ { "CNTNOTCMPLT", 0x08, 0x08 },
+ { "SCDATBUCKET", 0x10, 0x10 },
+ { "SCADERR", 0x20, 0x20 },
+ { "SCBCERR", 0x40, 0x40 },
+ { "STAETERM", 0x80, 0x80 }
+};
+
+int
+ahd_dchspltstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHSPLTSTAT0_parse_table, 8, "DCHSPLTSTAT0",
+ 0x96, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHSPLTSTAT1_parse_table[] = {
+ { "RXDATABUCKET", 0x01, 0x01 }
+};
+
+int
+ahd_dchspltstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHSPLTSTAT1_parse_table, 1, "DCHSPLTSTAT1",
+ 0x97, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCSPLTSTAT1_parse_table[] = {
+ { "RXDATABUCKET", 0x01, 0x01 }
+};
+
+int
+ahd_cmcspltstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCSPLTSTAT1_parse_table, 1, "CMCSPLTSTAT1",
+ 0x97, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYSPLTSTAT1_parse_table[] = {
+ { "RXDATABUCKET", 0x01, 0x01 }
+};
+
+int
+ahd_ovlyspltstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYSPLTSTAT1_parse_table, 1, "OVLYSPLTSTAT1",
+ 0x97, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGRXMSG0_parse_table[] = {
+ { "CFNUM", 0x07, 0x07 },
+ { "CDNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_sgrxmsg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGRXMSG0_parse_table, 2, "SGRXMSG0",
+ 0x98, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTADR0_parse_table[] = {
+ { "LOWER_ADDR", 0x7f, 0x7f }
+};
+
+int
+ahd_slvspltoutadr0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTADR0_parse_table, 1, "SLVSPLTOUTADR0",
+ 0x98, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGRXMSG1_parse_table[] = {
+ { "CBNUM", 0xff, 0xff }
+};
+
+int
+ahd_sgrxmsg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGRXMSG1_parse_table, 1, "SGRXMSG1",
+ 0x99, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTADR1_parse_table[] = {
+ { "REQ_FNUM", 0x07, 0x07 },
+ { "REQ_DNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_slvspltoutadr1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTADR1_parse_table, 2, "SLVSPLTOUTADR1",
+ 0x99, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGRXMSG2_parse_table[] = {
+ { "MINDEX", 0xff, 0xff }
+};
+
+int
+ahd_sgrxmsg2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGRXMSG2_parse_table, 1, "SGRXMSG2",
+ 0x9a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTADR2_parse_table[] = {
+ { "REQ_BNUM", 0xff, 0xff }
+};
+
+int
+ahd_slvspltoutadr2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTADR2_parse_table, 1, "SLVSPLTOUTADR2",
+ 0x9a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGRXMSG3_parse_table[] = {
+ { "MCLASS", 0x0f, 0x0f }
+};
+
+int
+ahd_sgrxmsg3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGRXMSG3_parse_table, 1, "SGRXMSG3",
+ 0x9b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTADR3_parse_table[] = {
+ { "RLXORD", 0x10, 0x10 },
+ { "TAG_NUM", 0x1f, 0x1f }
+};
+
+int
+ahd_slvspltoutadr3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTADR3_parse_table, 2, "SLVSPLTOUTADR3",
+ 0x9b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sgseqbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SGSEQBCNT",
+ 0x9c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTATTR0_parse_table[] = {
+ { "LOWER_BCNT", 0xff, 0xff }
+};
+
+int
+ahd_slvspltoutattr0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTATTR0_parse_table, 1, "SLVSPLTOUTATTR0",
+ 0x9c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTATTR1_parse_table[] = {
+ { "CMPLT_FNUM", 0x07, 0x07 },
+ { "CMPLT_DNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_slvspltoutattr1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTATTR1_parse_table, 2, "SLVSPLTOUTATTR1",
+ 0x9d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTATTR2_parse_table[] = {
+ { "CMPLT_BNUM", 0xff, 0xff }
+};
+
+int
+ahd_slvspltoutattr2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTATTR2_parse_table, 1, "SLVSPLTOUTATTR2",
+ 0x9e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGSPLTSTAT0_parse_table[] = {
+ { "RXSPLTRSP", 0x01, 0x01 },
+ { "RXSCEMSG", 0x02, 0x02 },
+ { "RXOVRUN", 0x04, 0x04 },
+ { "CNTNOTCMPLT", 0x08, 0x08 },
+ { "SCDATBUCKET", 0x10, 0x10 },
+ { "SCADERR", 0x20, 0x20 },
+ { "SCBCERR", 0x40, 0x40 },
+ { "STAETERM", 0x80, 0x80 }
+};
+
+int
+ahd_sgspltstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGSPLTSTAT0_parse_table, 8, "SGSPLTSTAT0",
+ 0x9e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SFUNCT_parse_table[] = {
+ { "TEST_NUM", 0x0f, 0x0f },
+ { "TEST_GROUP", 0xf0, 0xf0 }
+};
+
+int
+ahd_sfunct_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SFUNCT_parse_table, 2, "SFUNCT",
+ 0x9f, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGSPLTSTAT1_parse_table[] = {
+ { "RXDATABUCKET", 0x01, 0x01 }
+};
+
+int
+ahd_sgspltstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGSPLTSTAT1_parse_table, 1, "SGSPLTSTAT1",
+ 0x9f, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DF0PCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "TWATERR", 0x02, 0x02 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_df0pcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DF0PCISTAT_parse_table, 8, "DF0PCISTAT",
+ 0xa0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_reg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "REG0",
+ 0xa0, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DF1PCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "TWATERR", 0x02, 0x02 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_df1pcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DF1PCISTAT_parse_table, 8, "DF1PCISTAT",
+ 0xa1, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGPCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_sgpcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGPCISTAT_parse_table, 7, "SGPCISTAT",
+ 0xa2, regvalue, cur_col, wrap));
+}
+
+int
+ahd_reg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "REG1",
+ 0xa2, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCPCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "TWATERR", 0x02, 0x02 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_cmcpcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCPCISTAT_parse_table, 8, "CMCPCISTAT",
+ 0xa3, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYPCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_ovlypcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYPCISTAT_parse_table, 7, "OVLYPCISTAT",
+ 0xa4, regvalue, cur_col, wrap));
+}
+
+int
+ahd_reg_isr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "REG_ISR",
+ 0xa4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SG_STATE_parse_table[] = {
+ { "SEGS_AVAIL", 0x01, 0x01 },
+ { "LOADING_NEEDED", 0x02, 0x02 },
+ { "FETCH_INPROG", 0x04, 0x04 }
+};
+
+int
+ahd_sg_state_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SG_STATE_parse_table, 3, "SG_STATE",
+ 0xa6, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t MSIPCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "TWATERR", 0x02, 0x02 },
+ { "CLRPENDMSI", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 }
+};
+
+int
+ahd_msipcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(MSIPCISTAT_parse_table, 6, "MSIPCISTAT",
+ 0xa6, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t TARGPCISTAT_parse_table[] = {
+ { "TWATERR", 0x02, 0x02 },
+ { "STA", 0x08, 0x08 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_targpcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(TARGPCISTAT_parse_table, 4, "TARGPCISTAT",
+ 0xa7, regvalue, cur_col, wrap));
+}
+
+int
+ahd_data_count_odd_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DATA_COUNT_ODD",
+ 0xa7, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scbptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCBPTR",
+ 0xa8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccscbacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSCBACNT",
+ 0xab, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCBAUTOPTR_parse_table[] = {
+ { "SCBPTR_OFF", 0x07, 0x07 },
+ { "SCBPTR_ADDR", 0x38, 0x38 },
+ { "AUSCBPTR_EN", 0x80, 0x80 }
+};
+
+int
+ahd_scbautoptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCBAUTOPTR_parse_table, 3, "SCBAUTOPTR",
+ 0xab, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccsgaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSGADDR",
+ 0xac, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccscbaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSCBADDR",
+ 0xac, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccscbadr_bk_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSCBADR_BK",
+ 0xac, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMC_RAMBIST_parse_table[] = {
+ { "CMC_BUFFER_BIST_EN", 0x01, 0x01 },
+ { "CMC_BUFFER_BIST_FAIL",0x02, 0x02 },
+ { "SG_BIST_EN", 0x10, 0x10 },
+ { "SG_BIST_FAIL", 0x20, 0x20 },
+ { "SCBRAMBIST_FAIL", 0x40, 0x40 },
+ { "SG_ELEMENT_SIZE", 0x80, 0x80 }
+};
+
+int
+ahd_cmc_rambist_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMC_RAMBIST_parse_table, 6, "CMC_RAMBIST",
+ 0xad, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CCSGCTL_parse_table[] = {
+ { "CCSGRESET", 0x01, 0x01 },
+ { "SG_FETCH_REQ", 0x02, 0x02 },
+ { "CCSGENACK", 0x08, 0x08 },
+ { "SG_CACHE_AVAIL", 0x10, 0x10 },
+ { "CCSGDONE", 0x80, 0x80 },
+ { "CCSGEN", 0x0c, 0x0c }
+};
+
+int
+ahd_ccsgctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CCSGCTL_parse_table, 6, "CCSGCTL",
+ 0xad, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CCSCBCTL_parse_table[] = {
+ { "CCSCBRESET", 0x01, 0x01 },
+ { "CCSCBDIR", 0x04, 0x04 },
+ { "CCSCBEN", 0x08, 0x08 },
+ { "CCARREN", 0x10, 0x10 },
+ { "ARRDONE", 0x40, 0x40 },
+ { "CCSCBDONE", 0x80, 0x80 }
+};
+
+int
+ahd_ccscbctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CCSCBCTL_parse_table, 6, "CCSCBCTL",
+ 0xad, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccsgram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSGRAM",
+ 0xb0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_flexadr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FLEXADR",
+ 0xb0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccscbram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSCBRAM",
+ 0xb0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_flexcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FLEXCNT",
+ 0xb3, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t FLEXDMASTAT_parse_table[] = {
+ { "FLEXDMADONE", 0x01, 0x01 },
+ { "FLEXDMAERR", 0x02, 0x02 }
+};
+
+int
+ahd_flexdmastat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(FLEXDMASTAT_parse_table, 2, "FLEXDMASTAT",
+ 0xb5, regvalue, cur_col, wrap));
+}
+
+int
+ahd_flexdata_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FLEXDATA",
+ 0xb6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_brddat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "BRDDAT",
+ 0xb8, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t BRDCTL_parse_table[] = {
+ { "BRDSTB", 0x01, 0x01 },
+ { "BRDRW", 0x02, 0x02 },
+ { "BRDEN", 0x04, 0x04 },
+ { "BRDADDR", 0x38, 0x38 },
+ { "FLXARBREQ", 0x40, 0x40 },
+ { "FLXARBACK", 0x80, 0x80 }
+};
+
+int
+ahd_brdctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(BRDCTL_parse_table, 6, "BRDCTL",
+ 0xb9, regvalue, cur_col, wrap));
+}
+
+int
+ahd_seeadr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SEEADR",
+ 0xba, regvalue, cur_col, wrap));
+}
+
+int
+ahd_seedat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SEEDAT",
+ 0xbc, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEECTL_parse_table[] = {
+ { "SEEOP_ERAL", 0x40, 0x70 },
+ { "SEEOP_WRITE", 0x50, 0x70 },
+ { "SEEOP_READ", 0x60, 0x70 },
+ { "SEEOP_ERASE", 0x70, 0x70 },
+ { "SEESTART", 0x01, 0x01 },
+ { "SEERST", 0x02, 0x02 },
+ { "SEEOPCODE", 0x70, 0x70 },
+ { "SEEOP_EWEN", 0x40, 0x40 },
+ { "SEEOP_WALL", 0x40, 0x40 },
+ { "SEEOP_EWDS", 0x40, 0x40 }
+};
+
+int
+ahd_seectl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEECTL_parse_table, 10, "SEECTL",
+ 0xbe, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEESTAT_parse_table[] = {
+ { "SEESTART", 0x01, 0x01 },
+ { "SEEBUSY", 0x02, 0x02 },
+ { "SEEARBACK", 0x04, 0x04 },
+ { "LDALTID_L", 0x08, 0x08 },
+ { "SEEOPCODE", 0x70, 0x70 },
+ { "INIT_DONE", 0x80, 0x80 }
+};
+
+int
+ahd_seestat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEESTAT_parse_table, 6, "SEESTAT",
+ 0xbe, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCBCNT",
+ 0xbf, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfwaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFWADDR",
+ 0xc0, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPFLTRCTL_parse_table[] = {
+ { "DSPFCNTSEL", 0x0f, 0x0f },
+ { "EDGESENSE", 0x10, 0x10 },
+ { "FLTRDISABLE", 0x20, 0x20 }
+};
+
+int
+ahd_dspfltrctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPFLTRCTL_parse_table, 3, "DSPFLTRCTL",
+ 0xc0, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPDATACTL_parse_table[] = {
+ { "XMITOFFSTDIS", 0x02, 0x02 },
+ { "RCVROFFSTDIS", 0x04, 0x04 },
+ { "DESQDIS", 0x10, 0x10 },
+ { "BYPASSENAB", 0x80, 0x80 }
+};
+
+int
+ahd_dspdatactl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPDATACTL_parse_table, 4, "DSPDATACTL",
+ 0xc1, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfraddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFRADDR",
+ 0xc2, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPREQCTL_parse_table[] = {
+ { "MANREQDLY", 0x3f, 0x3f },
+ { "MANREQCTL", 0xc0, 0xc0 }
+};
+
+int
+ahd_dspreqctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPREQCTL_parse_table, 2, "DSPREQCTL",
+ 0xc2, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPACKCTL_parse_table[] = {
+ { "MANACKDLY", 0x3f, 0x3f },
+ { "MANACKCTL", 0xc0, 0xc0 }
+};
+
+int
+ahd_dspackctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPACKCTL_parse_table, 2, "DSPACKCTL",
+ 0xc3, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfdat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFDAT",
+ 0xc4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPSELECT_parse_table[] = {
+ { "DSPSEL", 0x1f, 0x1f },
+ { "AUTOINCEN", 0x80, 0x80 }
+};
+
+int
+ahd_dspselect_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPSELECT_parse_table, 2, "DSPSELECT",
+ 0xc4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t WRTBIASCTL_parse_table[] = {
+ { "XMITMANVAL", 0x3f, 0x3f },
+ { "AUTOXBCDIS", 0x80, 0x80 }
+};
+
+int
+ahd_wrtbiasctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(WRTBIASCTL_parse_table, 2, "WRTBIASCTL",
+ 0xc5, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t RCVRBIOSCTL_parse_table[] = {
+ { "RCVRMANVAL", 0x3f, 0x3f },
+ { "AUTORBCDIS", 0x80, 0x80 }
+};
+
+int
+ahd_rcvrbiosctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(RCVRBIOSCTL_parse_table, 2, "RCVRBIOSCTL",
+ 0xc6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_wrtbiascalc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "WRTBIASCALC",
+ 0xc7, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfptrs_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFPTRS",
+ 0xc8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_rcvrbiascalc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "RCVRBIASCALC",
+ 0xc8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfbkptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFBKPTR",
+ 0xc9, regvalue, cur_col, wrap));
+}
+
+int
+ahd_skewcalc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SKEWCALC",
+ 0xc9, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFDBCTL_parse_table[] = {
+ { "DFF_RAMBIST_EN", 0x01, 0x01 },
+ { "DFF_RAMBIST_DONE", 0x02, 0x02 },
+ { "DFF_RAMBIST_FAIL", 0x04, 0x04 },
+ { "DFF_DIR_ERR", 0x08, 0x08 },
+ { "DFF_CIO_RD_RDY", 0x10, 0x10 },
+ { "DFF_CIO_WR_RDY", 0x20, 0x20 }
+};
+
+int
+ahd_dfdbctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFDBCTL_parse_table, 6, "DFDBCTL",
+ 0xcb, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfscnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFSCNT",
+ 0xcc, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFBCNT",
+ 0xce, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ovlyaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "OVLYADDR",
+ 0xd4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQCTL0_parse_table[] = {
+ { "LOADRAM", 0x01, 0x01 },
+ { "SEQRESET", 0x02, 0x02 },
+ { "STEP", 0x04, 0x04 },
+ { "BRKADRINTEN", 0x08, 0x08 },
+ { "FASTMODE", 0x10, 0x10 },
+ { "FAILDIS", 0x20, 0x20 },
+ { "PAUSEDIS", 0x40, 0x40 },
+ { "PERRORDIS", 0x80, 0x80 }
+};
+
+int
+ahd_seqctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQCTL0_parse_table, 8, "SEQCTL0",
+ 0xd6, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQCTL1_parse_table[] = {
+ { "RAMBIST_EN", 0x01, 0x01 },
+ { "RAMBIST_FAIL", 0x02, 0x02 },
+ { "RAMBIST_DONE", 0x04, 0x04 },
+ { "OVRLAY_DATA_CHK", 0x08, 0x08 }
+};
+
+int
+ahd_seqctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQCTL1_parse_table, 4, "SEQCTL1",
+ 0xd7, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t FLAGS_parse_table[] = {
+ { "CARRY", 0x01, 0x01 },
+ { "ZERO", 0x02, 0x02 }
+};
+
+int
+ahd_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(FLAGS_parse_table, 2, "FLAGS",
+ 0xd8, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQINTCTL_parse_table[] = {
+ { "IRET", 0x01, 0x01 },
+ { "INTMASK1", 0x02, 0x02 },
+ { "INTMASK2", 0x04, 0x04 },
+ { "SCS_SEQ_INT1M0", 0x08, 0x08 },
+ { "SCS_SEQ_INT1M1", 0x10, 0x10 },
+ { "INT1_CONTEXT", 0x20, 0x20 },
+ { "INTVEC1DSL", 0x80, 0x80 }
+};
+
+int
+ahd_seqintctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQINTCTL_parse_table, 7, "SEQINTCTL",
+ 0xd9, regvalue, cur_col, wrap));
+}
+
+int
+ahd_seqram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SEQRAM",
+ 0xda, regvalue, cur_col, wrap));
+}
+
+int
+ahd_prgmcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "PRGMCNT",
+ 0xde, regvalue, cur_col, wrap));
+}
+
+int
+ahd_accum_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ACCUM",
+ 0xe0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sindex_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SINDEX",
+ 0xe2, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dindex_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DINDEX",
+ 0xe4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t BRKADDR1_parse_table[] = {
+ { "BRKDIS", 0x80, 0x80 }
+};
+
+int
+ahd_brkaddr1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(BRKADDR1_parse_table, 1, "BRKADDR1",
+ 0xe6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_brkaddr0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "BRKADDR0",
+ 0xe6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_allones_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ALLONES",
+ 0xe8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_allzeros_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ALLZEROS",
+ 0xea, regvalue, cur_col, wrap));
+}
+
+int
+ahd_none_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NONE",
+ 0xea, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sindir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SINDIR",
+ 0xec, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dindir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DINDIR",
+ 0xed, regvalue, cur_col, wrap));
+}
+
+int
+ahd_function1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FUNCTION1",
+ 0xf0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_stack_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "STACK",
+ 0xf2, regvalue, cur_col, wrap));
+}
+
+int
+ahd_curaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CURADDR",
+ 0xf4, regvalue, cur_col, wrap));
+}
+
+int
+ahd_intvec1_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INTVEC1_ADDR",
+ 0xf4, regvalue, cur_col, wrap));
+}
+
+int
+ahd_intvec2_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INTVEC2_ADDR",
+ 0xf6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lastaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LASTADDR",
+ 0xf6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_longjmp_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LONGJMP_ADDR",
+ 0xf8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_longjmp_scb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LONGJMP_SCB",
+ 0xfa, regvalue, cur_col, wrap));
+}
+
+int
+ahd_accum_save_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ACCUM_SAVE",
+ 0xfc, regvalue, cur_col, wrap));
+}
+
+int
+ahd_waiting_scb_tails_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "WAITING_SCB_TAILS",
+ 0x100, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ahd_pci_config_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "AHD_PCI_CONFIG_BASE",
+ 0x100, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sram_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SRAM_BASE",
+ 0x100, regvalue, cur_col, wrap));
+}
+
+int
+ahd_waiting_tid_head_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "WAITING_TID_HEAD",
+ 0x120, regvalue, cur_col, wrap));
+}
+
+int
+ahd_waiting_tid_tail_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "WAITING_TID_TAIL",
+ 0x122, regvalue, cur_col, wrap));
+}
+
+int
+ahd_next_queued_scb_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEXT_QUEUED_SCB_ADDR",
+ 0x124, regvalue, cur_col, wrap));
+}
+
+int
+ahd_complete_scb_head_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "COMPLETE_SCB_HEAD",
+ 0x128, regvalue, cur_col, wrap));
+}
+
+int
+ahd_complete_scb_dmainprog_head_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "COMPLETE_SCB_DMAINPROG_HEAD",
+ 0x12a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_complete_dma_scb_head_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "COMPLETE_DMA_SCB_HEAD",
+ 0x12c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_qfreeze_count_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "QFREEZE_COUNT",
+ 0x12e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_saved_mode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SAVED_MODE",
+ 0x130, regvalue, cur_col, wrap));
+}
+
+int
+ahd_msg_out_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MSG_OUT",
+ 0x131, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DMAPARAMS_parse_table[] = {
+ { "FIFORESET", 0x01, 0x01 },
+ { "FIFOFLUSH", 0x02, 0x02 },
+ { "DIRECTION", 0x04, 0x04 },
+ { "HDMAEN", 0x08, 0x08 },
+ { "HDMAENACK", 0x08, 0x08 },
+ { "SDMAEN", 0x10, 0x10 },
+ { "SDMAENACK", 0x10, 0x10 },
+ { "SCSIEN", 0x20, 0x20 },
+ { "WIDEODD", 0x40, 0x40 },
+ { "PRELOADEN", 0x80, 0x80 }
+};
+
+int
+ahd_dmaparams_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DMAPARAMS_parse_table, 10, "DMAPARAMS",
+ 0x132, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQ_FLAGS_parse_table[] = {
+ { "NO_DISCONNECT", 0x01, 0x01 },
+ { "SPHASE_PENDING", 0x02, 0x02 },
+ { "DPHASE_PENDING", 0x04, 0x04 },
+ { "CMDPHASE_PENDING", 0x08, 0x08 },
+ { "TARG_CMD_PENDING", 0x10, 0x10 },
+ { "DPHASE", 0x20, 0x20 },
+ { "NO_CDB_SENT", 0x40, 0x40 },
+ { "TARGET_CMD_IS_TAGGED",0x40, 0x40 },
+ { "NOT_IDENTIFIED", 0x80, 0x80 }
+};
+
+int
+ahd_seq_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQ_FLAGS_parse_table, 9, "SEQ_FLAGS",
+ 0x133, regvalue, cur_col, wrap));
+}
+
+int
+ahd_saved_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SAVED_SCSIID",
+ 0x134, regvalue, cur_col, wrap));
+}
+
+int
+ahd_saved_lun_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SAVED_LUN",
+ 0x135, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LASTPHASE_parse_table[] = {
+ { "P_DATAOUT", 0x00, 0xe0 },
+ { "P_DATAOUT_DT", 0x20, 0xe0 },
+ { "P_DATAIN", 0x40, 0xe0 },
+ { "P_DATAIN_DT", 0x60, 0xe0 },
+ { "P_COMMAND", 0x80, 0xe0 },
+ { "P_MESGOUT", 0xa0, 0xe0 },
+ { "P_STATUS", 0xc0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 },
+ { "P_BUSFREE", 0x01, 0x01 },
+ { "MSGI", 0x20, 0x20 },
+ { "IOI", 0x40, 0x40 },
+ { "CDI", 0x80, 0x80 },
+ { "PHASE_MASK", 0xe0, 0xe0 }
+};
+
+int
+ahd_lastphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LASTPHASE_parse_table, 13, "LASTPHASE",
+ 0x136, regvalue, cur_col, wrap));
+}
+
+int
+ahd_qoutfifo_entry_valid_tag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "QOUTFIFO_ENTRY_VALID_TAG",
+ 0x137, regvalue, cur_col, wrap));
+}
+
+int
+ahd_shared_data_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SHARED_DATA_ADDR",
+ 0x138, regvalue, cur_col, wrap));
+}
+
+int
+ahd_qoutfifo_next_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "QOUTFIFO_NEXT_ADDR",
+ 0x13c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_kernel_tqinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "KERNEL_TQINPOS",
+ 0x140, regvalue, cur_col, wrap));
+}
+
+int
+ahd_tqinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "TQINPOS",
+ 0x141, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ARG_1_parse_table[] = {
+ { "CONT_MSG_LOOP_TARG", 0x02, 0x02 },
+ { "CONT_MSG_LOOP_READ", 0x03, 0x03 },
+ { "CONT_MSG_LOOP_WRITE",0x04, 0x04 },
+ { "EXIT_MSG_LOOP", 0x08, 0x08 },
+ { "MSGOUT_PHASEMIS", 0x10, 0x10 },
+ { "SEND_REJ", 0x20, 0x20 },
+ { "SEND_SENSE", 0x40, 0x40 },
+ { "SEND_MSG", 0x80, 0x80 }
+};
+
+int
+ahd_arg_1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ARG_1_parse_table, 8, "ARG_1",
+ 0x142, regvalue, cur_col, wrap));
+}
+
+int
+ahd_arg_2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ARG_2",
+ 0x143, regvalue, cur_col, wrap));
+}
+
+int
+ahd_last_msg_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LAST_MSG",
+ 0x144, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISEQ_TEMPLATE_parse_table[] = {
+ { "ALTSTIM", 0x01, 0x01 },
+ { "ENAUTOATNP", 0x02, 0x02 },
+ { "MANUALP", 0x0c, 0x0c },
+ { "ENRSELI", 0x10, 0x10 },
+ { "ENSELI", 0x20, 0x20 },
+ { "MANUALCTL", 0x40, 0x40 }
+};
+
+int
+ahd_scsiseq_template_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISEQ_TEMPLATE_parse_table, 6, "SCSISEQ_TEMPLATE",
+ 0x145, regvalue, cur_col, wrap));
+}
+
+int
+ahd_initiator_tag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INITIATOR_TAG",
+ 0x146, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQ_FLAGS2_parse_table[] = {
+ { "TARGET_MSG_PENDING", 0x02, 0x02 },
+ { "SELECTOUT_QFROZEN", 0x04, 0x04 }
+};
+
+int
+ahd_seq_flags2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQ_FLAGS2_parse_table, 2, "SEQ_FLAGS2",
+ 0x147, regvalue, cur_col, wrap));
+}
+
+int
+ahd_allocfifo_scbptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ALLOCFIFO_SCBPTR",
+ 0x148, regvalue, cur_col, wrap));
+}
+
+int
+ahd_int_coalessing_timer_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INT_COALESSING_TIMER",
+ 0x14a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_int_coalessing_maxcmds_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INT_COALESSING_MAXCMDS",
+ 0x14c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_int_coalessing_mincmds_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INT_COALESSING_MINCMDS",
+ 0x14d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmds_pending_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDS_PENDING",
+ 0x14e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_int_coalessing_cmdcount_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INT_COALESSING_CMDCOUNT",
+ 0x150, regvalue, cur_col, wrap));
+}
+
+int
+ahd_local_hs_mailbox_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LOCAL_HS_MAILBOX",
+ 0x151, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmdsize_table_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDSIZE_TABLE",
+ 0x152, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_BASE",
+ 0x180, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_residual_datacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_RESIDUAL_DATACNT",
+ 0x180, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_RESIDUAL_SGPTR_parse_table[] = {
+ { "SG_LIST_NULL", 0x01, 0x01 },
+ { "SG_OVERRUN_RESID", 0x02, 0x02 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahd_scb_residual_sgptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_RESIDUAL_SGPTR_parse_table, 3, "SCB_RESIDUAL_SGPTR",
+ 0x184, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_scsi_status_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_SCSI_STATUS",
+ 0x188, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_target_phases_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TARGET_PHASES",
+ 0x189, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_target_data_dir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TARGET_DATA_DIR",
+ 0x18a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_target_itag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TARGET_ITAG",
+ 0x18b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_sense_busaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_SENSE_BUSADDR",
+ 0x18c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_tag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TAG",
+ 0x190, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_CDB_LEN_parse_table[] = {
+ { "SCB_CDB_LEN_PTR", 0x80, 0x80 }
+};
+
+int
+ahd_scb_cdb_len_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_CDB_LEN_parse_table, 1, "SCB_CDB_LEN",
+ 0x192, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_task_management_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TASK_MANAGEMENT",
+ 0x193, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_next_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_NEXT",
+ 0x194, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_next2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_NEXT2",
+ 0x196, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_dataptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_DATAPTR",
+ 0x198, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_DATACNT_parse_table[] = {
+ { "SG_HIGH_ADDR_BITS", 0x7f, 0x7f },
+ { "SG_LAST_SEG", 0x80, 0x80 }
+};
+
+int
+ahd_scb_datacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_DATACNT_parse_table, 2, "SCB_DATACNT",
+ 0x1a0, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_SGPTR_parse_table[] = {
+ { "SG_LIST_NULL", 0x01, 0x01 },
+ { "SG_FULL_RESID", 0x02, 0x02 },
+ { "SG_STATUS_VALID", 0x04, 0x04 }
+};
+
+int
+ahd_scb_sgptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_SGPTR_parse_table, 3, "SCB_SGPTR",
+ 0x1a4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_CONTROL_parse_table[] = {
+ { "SCB_TAG_TYPE", 0x03, 0x03 },
+ { "DISCONNECTED", 0x04, 0x04 },
+ { "STATUS_RCVD", 0x08, 0x08 },
+ { "MK_MESSAGE", 0x10, 0x10 },
+ { "TAG_ENB", 0x20, 0x20 },
+ { "DISCENB", 0x40, 0x40 },
+ { "TARGET_SCB", 0x80, 0x80 }
+};
+
+int
+ahd_scb_control_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_CONTROL_parse_table, 7, "SCB_CONTROL",
+ 0x1a8, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_SCSIID_parse_table[] = {
+ { "OID", 0x0f, 0x0f },
+ { "TID", 0xf0, 0xf0 }
+};
+
+int
+ahd_scb_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_SCSIID_parse_table, 2, "SCB_SCSIID",
+ 0x1a9, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_LUN_parse_table[] = {
+ { "LID", 0xff, 0xff }
+};
+
+int
+ahd_scb_lun_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_LUN_parse_table, 1, "SCB_LUN",
+ 0x1aa, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_task_attribute_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TASK_ATTRIBUTE",
+ 0x1ab, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_busaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_BUSADDR",
+ 0x1ac, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_spare_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_SPARE",
+ 0x1b0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_disconnected_lists_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_DISCONNECTED_LISTS",
+ 0x1b8, regvalue, cur_col, wrap));
+}
+
diff --git a/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
new file mode 100644
index 000000000000..256a2d10ad64
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
@@ -0,0 +1,1067 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#77 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#59 $
+ */
+static uint8_t seqprog[] = {
+ 0xff, 0x02, 0x06, 0x78,
+ 0x00, 0xea, 0x3c, 0x59,
+ 0x01, 0xea, 0x04, 0x30,
+ 0xff, 0x04, 0x0c, 0x78,
+ 0x17, 0xea, 0x3c, 0x59,
+ 0x17, 0xea, 0x04, 0x00,
+ 0x33, 0xea, 0x30, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x60, 0x3a, 0x1a, 0x68,
+ 0x04, 0x47, 0x1b, 0x68,
+ 0xff, 0x21, 0x1b, 0x70,
+ 0x40, 0x4b, 0x7e, 0x69,
+ 0x00, 0xe2, 0x40, 0x59,
+ 0x40, 0x4b, 0x7e, 0x69,
+ 0x20, 0x4b, 0x6e, 0x69,
+ 0xfc, 0x42, 0x24, 0x78,
+ 0x10, 0x40, 0x24, 0x78,
+ 0x00, 0xe2, 0x8c, 0x5d,
+ 0x20, 0x4d, 0x28, 0x78,
+ 0x00, 0xe2, 0x8c, 0x5d,
+ 0x00, 0xe2, 0x34, 0x58,
+ 0x00, 0xe2, 0x5c, 0x58,
+ 0x00, 0xe2, 0x6c, 0x58,
+ 0x00, 0xe2, 0x06, 0x40,
+ 0x33, 0xea, 0x30, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x01, 0x52, 0x4e, 0x7d,
+ 0x02, 0x58, 0x50, 0x31,
+ 0xff, 0xea, 0x10, 0x0b,
+ 0x08, 0xa8, 0x51, 0x03,
+ 0x01, 0xa4, 0x43, 0x78,
+ 0x00, 0xe2, 0x32, 0x5b,
+ 0x00, 0xe2, 0x34, 0x40,
+ 0xff, 0xea, 0xd4, 0x19,
+ 0x02, 0xa8, 0x84, 0x32,
+ 0x00, 0xea, 0x30, 0x59,
+ 0x01, 0xea, 0x00, 0x30,
+ 0x00, 0xe2, 0x80, 0x5d,
+ 0x00, 0xe2, 0x4e, 0x4d,
+ 0x11, 0xea, 0x30, 0x59,
+ 0x11, 0xea, 0x00, 0x00,
+ 0x00, 0xe2, 0x80, 0x5d,
+ 0x00, 0xe2, 0x4e, 0x4d,
+ 0x33, 0xea, 0x30, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x00, 0xe2, 0x28, 0x43,
+ 0x00, 0xea, 0x30, 0x59,
+ 0x01, 0xea, 0x00, 0x30,
+ 0x80, 0xf9, 0x64, 0x68,
+ 0x00, 0xe2, 0x2e, 0x59,
+ 0x11, 0xea, 0x30, 0x59,
+ 0x11, 0xea, 0x00, 0x00,
+ 0x80, 0xf9, 0x2e, 0x79,
+ 0xff, 0xea, 0xd4, 0x0d,
+ 0x22, 0xea, 0x30, 0x59,
+ 0x22, 0xea, 0x00, 0x00,
+ 0x10, 0x16, 0x76, 0x78,
+ 0x01, 0x0b, 0xa2, 0x32,
+ 0x10, 0x16, 0x2c, 0x00,
+ 0x18, 0xad, 0xe4, 0x78,
+ 0x04, 0xad, 0xb2, 0x68,
+ 0x80, 0xad, 0x4e, 0x7d,
+ 0x10, 0xad, 0x80, 0x78,
+ 0xe7, 0xad, 0x5a, 0x0d,
+ 0xe7, 0xad, 0x5a, 0x09,
+ 0x00, 0xe2, 0x8e, 0x58,
+ 0xff, 0xea, 0x56, 0x02,
+ 0x04, 0x7c, 0x78, 0x32,
+ 0x20, 0x16, 0x4e, 0x7d,
+ 0x04, 0x38, 0x79, 0x32,
+ 0x80, 0x37, 0x6f, 0x16,
+ 0xff, 0x2d, 0x9d, 0x60,
+ 0xff, 0x29, 0x9d, 0x60,
+ 0x40, 0x51, 0xad, 0x78,
+ 0xff, 0x4f, 0x9d, 0x68,
+ 0xff, 0x4d, 0xc1, 0x19,
+ 0x00, 0x4e, 0xd5, 0x19,
+ 0x00, 0xe2, 0xac, 0x50,
+ 0x01, 0x4c, 0xc1, 0x31,
+ 0x00, 0x50, 0xd5, 0x19,
+ 0x00, 0xe2, 0xac, 0x48,
+ 0x80, 0x18, 0x4e, 0x7d,
+ 0x02, 0x4a, 0x1d, 0x30,
+ 0x10, 0xea, 0x18, 0x00,
+ 0x60, 0x18, 0x30, 0x00,
+ 0x7f, 0x18, 0x30, 0x0c,
+ 0x02, 0xea, 0x02, 0x00,
+ 0xff, 0xea, 0xa0, 0x0a,
+ 0x80, 0x18, 0x30, 0x04,
+ 0x40, 0xad, 0x4e, 0x7d,
+ 0xe7, 0xad, 0x5a, 0x09,
+ 0x02, 0xa8, 0x40, 0x31,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x01, 0x4e, 0x9d, 0x1a,
+ 0x00, 0x4f, 0x9f, 0x22,
+ 0x04, 0x94, 0x49, 0x32,
+ 0xff, 0xea, 0x2a, 0x03,
+ 0xff, 0xea, 0x2e, 0x03,
+ 0x01, 0x10, 0xd4, 0x31,
+ 0x10, 0xa8, 0xd9, 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, 0xd8, 0x70,
+ 0x02, 0xa0, 0x28, 0x37,
+ 0xff, 0x21, 0xe1, 0x70,
+ 0x02, 0x22, 0x51, 0x31,
+ 0x02, 0xa0, 0x2c, 0x33,
+ 0x02, 0xa0, 0x44, 0x36,
+ 0x02, 0xa0, 0x40, 0x32,
+ 0x02, 0xa0, 0x44, 0x36,
+ 0x04, 0x47, 0xe9, 0x68,
+ 0x40, 0x16, 0x14, 0x69,
+ 0xff, 0x2d, 0x19, 0x61,
+ 0xff, 0x29, 0x4f, 0x75,
+ 0x01, 0x37, 0xc1, 0x31,
+ 0x02, 0x28, 0x55, 0x32,
+ 0x01, 0xea, 0x5a, 0x01,
+ 0x04, 0x3c, 0xf9, 0x30,
+ 0x02, 0x28, 0x51, 0x31,
+ 0x01, 0xa8, 0x60, 0x31,
+ 0x00, 0xa9, 0x60, 0x01,
+ 0x01, 0x14, 0xd4, 0x31,
+ 0x01, 0x50, 0xa1, 0x1a,
+ 0xff, 0x4e, 0x9d, 0x1a,
+ 0xff, 0x4f, 0x9f, 0x22,
+ 0xff, 0x8d, 0x0d, 0x71,
+ 0x80, 0xac, 0x0c, 0x71,
+ 0x20, 0x16, 0x0c, 0x69,
+ 0x02, 0x8c, 0x51, 0x31,
+ 0x00, 0xe2, 0xf6, 0x40,
+ 0x01, 0xac, 0x08, 0x31,
+ 0x09, 0xea, 0x5a, 0x01,
+ 0x02, 0x8c, 0x51, 0x32,
+ 0xff, 0xea, 0x1a, 0x07,
+ 0x04, 0x24, 0xf9, 0x30,
+ 0x1d, 0xea, 0x24, 0x41,
+ 0x02, 0x2c, 0x51, 0x31,
+ 0x04, 0xac, 0xf9, 0x30,
+ 0x19, 0xea, 0x24, 0x59,
+ 0x02, 0x8c, 0x59, 0x32,
+ 0x02, 0x28, 0x19, 0x33,
+ 0x02, 0xa8, 0x50, 0x36,
+ 0x06, 0xea, 0x08, 0x81,
+ 0x01, 0xe2, 0x5a, 0x35,
+ 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,
+ 0x80, 0xea, 0xb2, 0x01,
+ 0x11, 0x00, 0x00, 0x10,
+ 0xff, 0xea, 0xb2, 0x0d,
+ 0x01, 0xe2, 0x04, 0x30,
+ 0x01, 0xea, 0x04, 0x34,
+ 0x02, 0x20, 0xbd, 0x30,
+ 0x02, 0x20, 0xb9, 0x30,
+ 0x02, 0x20, 0x51, 0x31,
+ 0x4c, 0xa9, 0xd7, 0x28,
+ 0x10, 0xa8, 0x4f, 0x79,
+ 0x01, 0x6b, 0xc0, 0x30,
+ 0x02, 0x64, 0xc8, 0x00,
+ 0x40, 0x3a, 0x74, 0x04,
+ 0x00, 0xe2, 0x5c, 0x58,
+ 0x33, 0xea, 0x30, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x30, 0x3f, 0xc0, 0x09,
+ 0x30, 0xe0, 0x50, 0x61,
+ 0x20, 0x3f, 0x66, 0x69,
+ 0x10, 0x3f, 0x50, 0x79,
+ 0x02, 0xea, 0x7e, 0x00,
+ 0x00, 0xea, 0x30, 0x59,
+ 0x01, 0xea, 0x00, 0x30,
+ 0x02, 0x48, 0x51, 0x35,
+ 0x01, 0xea, 0x7e, 0x00,
+ 0x11, 0xea, 0x30, 0x59,
+ 0x11, 0xea, 0x00, 0x00,
+ 0x02, 0x48, 0x51, 0x35,
+ 0x08, 0xea, 0x98, 0x00,
+ 0x08, 0x57, 0xae, 0x00,
+ 0x08, 0x3c, 0x78, 0x00,
+ 0xf0, 0x49, 0x68, 0x0a,
+ 0x0f, 0x67, 0xc0, 0x09,
+ 0x00, 0x34, 0x69, 0x02,
+ 0x20, 0xea, 0x96, 0x00,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x40, 0x3a, 0x9a, 0x69,
+ 0x02, 0x55, 0x06, 0x68,
+ 0x02, 0x56, 0x9a, 0x69,
+ 0xff, 0x5b, 0x9a, 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, 0x9c, 0x79,
+ 0x02, 0x94, 0x51, 0x31,
+ 0x00, 0xe2, 0x92, 0x41,
+ 0x02, 0x5e, 0x50, 0x31,
+ 0x02, 0xa8, 0xb8, 0x30,
+ 0x02, 0x5c, 0x50, 0x31,
+ 0xff, 0x95, 0xad, 0x71,
+ 0x02, 0x94, 0x41, 0x31,
+ 0x02, 0x22, 0x51, 0x31,
+ 0x02, 0xa0, 0x2c, 0x33,
+ 0x02, 0xa0, 0x44, 0x32,
+ 0x00, 0xe2, 0xb6, 0x41,
+ 0x10, 0xa8, 0xb7, 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, 0xbf, 0x61,
+ 0xff, 0xea, 0x46, 0x02,
+ 0x02, 0x5c, 0x50, 0x31,
+ 0x40, 0xea, 0x96, 0x00,
+ 0x02, 0x56, 0x94, 0x6d,
+ 0x01, 0x55, 0x94, 0x6d,
+ 0x10, 0xa8, 0xcb, 0x79,
+ 0x10, 0x40, 0xcc, 0x69,
+ 0x01, 0x56, 0x06, 0x68,
+ 0xbf, 0x3a, 0x74, 0x08,
+ 0x08, 0xea, 0x98, 0x00,
+ 0x08, 0x57, 0xae, 0x00,
+ 0x01, 0xa9, 0x69, 0x32,
+ 0x01, 0xaa, 0x6b, 0x32,
+ 0x08, 0x3c, 0x78, 0x00,
+ 0x80, 0xea, 0x62, 0x02,
+ 0x40, 0xea, 0x66, 0x02,
+ 0x00, 0xe2, 0x92, 0x5b,
+ 0x01, 0x36, 0xc1, 0x31,
+ 0x9f, 0xe0, 0x26, 0x7c,
+ 0x80, 0xe0, 0xf0, 0x71,
+ 0xa0, 0xe0, 0x28, 0x72,
+ 0xc0, 0xe0, 0x1e, 0x72,
+ 0xe0, 0xe0, 0x58, 0x72,
+ 0x01, 0xea, 0x3c, 0x59,
+ 0x01, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x80, 0x33, 0xf7, 0x79,
+ 0x03, 0xea, 0x3c, 0x59,
+ 0x03, 0xea, 0x04, 0x00,
+ 0xee, 0x00, 0xfe, 0x69,
+ 0x05, 0xea, 0xb4, 0x00,
+ 0x33, 0xea, 0x30, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x02, 0xa8, 0x90, 0x32,
+ 0x00, 0xe2, 0x56, 0x59,
+ 0xef, 0x92, 0xd5, 0x19,
+ 0x00, 0xe2, 0x0e, 0x52,
+ 0x0b, 0x84, 0xe1, 0x30,
+ 0x02, 0xea, 0x36, 0x00,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0x00, 0xe2, 0x14, 0x42,
+ 0x01, 0x92, 0xd1, 0x30,
+ 0x10, 0x80, 0x89, 0x31,
+ 0x20, 0xea, 0x32, 0x00,
+ 0xbf, 0x33, 0x67, 0x0a,
+ 0x20, 0x19, 0x16, 0x6a,
+ 0x02, 0x4d, 0xdc, 0x69,
+ 0x40, 0x33, 0x67, 0x02,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x80, 0x33, 0x95, 0x6a,
+ 0x01, 0x44, 0x10, 0x33,
+ 0x08, 0xa8, 0x51, 0x03,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x10, 0xea, 0x80, 0x00,
+ 0x01, 0x31, 0xc5, 0x31,
+ 0x80, 0xe2, 0x44, 0x62,
+ 0x10, 0xa8, 0x69, 0x6a,
+ 0xc0, 0xaa, 0xc5, 0x01,
+ 0x40, 0xa8, 0x35, 0x6a,
+ 0xbf, 0xe2, 0xc4, 0x09,
+ 0x20, 0xa8, 0x49, 0x7a,
+ 0x01, 0xe2, 0x88, 0x30,
+ 0x00, 0xe2, 0x92, 0x5b,
+ 0xa0, 0x36, 0x51, 0x62,
+ 0x23, 0xa8, 0x89, 0x08,
+ 0x00, 0xe2, 0x92, 0x5b,
+ 0xa0, 0x36, 0x51, 0x62,
+ 0x00, 0xa8, 0x48, 0x42,
+ 0xff, 0xe2, 0x48, 0x62,
+ 0x00, 0xe2, 0x68, 0x42,
+ 0x40, 0xea, 0x98, 0x00,
+ 0x01, 0xe2, 0x88, 0x30,
+ 0x00, 0xe2, 0x92, 0x5b,
+ 0xa0, 0x36, 0x27, 0x72,
+ 0x40, 0xea, 0x98, 0x00,
+ 0x01, 0x31, 0x89, 0x32,
+ 0x08, 0xea, 0x62, 0x02,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0xe0, 0xea, 0xa2, 0x5b,
+ 0x80, 0xe0, 0xa0, 0x6a,
+ 0x04, 0xe0, 0x40, 0x73,
+ 0x02, 0xe0, 0x70, 0x73,
+ 0x00, 0xea, 0xfe, 0x72,
+ 0x03, 0xe0, 0x80, 0x73,
+ 0x23, 0xe0, 0x7a, 0x72,
+ 0x08, 0xe0, 0x9c, 0x72,
+ 0x00, 0xe2, 0x92, 0x5b,
+ 0x07, 0xea, 0x3c, 0x59,
+ 0x07, 0xea, 0x04, 0x00,
+ 0x08, 0x42, 0xdd, 0x71,
+ 0x04, 0x42, 0x77, 0x62,
+ 0x01, 0x43, 0x89, 0x30,
+ 0x00, 0xe2, 0x68, 0x42,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0x68, 0x42,
+ 0x01, 0x00, 0x60, 0x32,
+ 0x33, 0xea, 0x30, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x4c, 0x34, 0xc1, 0x28,
+ 0x01, 0x64, 0xc0, 0x31,
+ 0x00, 0x30, 0x31, 0x59,
+ 0x01, 0x30, 0x01, 0x30,
+ 0x01, 0xe0, 0x9a, 0x7a,
+ 0xa0, 0xea, 0x98, 0x5b,
+ 0x01, 0xa0, 0x9a, 0x62,
+ 0x01, 0x84, 0x93, 0x7a,
+ 0x01, 0xa7, 0x9c, 0x7a,
+ 0x00, 0xe2, 0x9c, 0x42,
+ 0x03, 0xea, 0x3c, 0x59,
+ 0x03, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x9c, 0x42,
+ 0x07, 0xea, 0xaa, 0x5b,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x3f, 0xe0, 0x6a, 0x0a,
+ 0xc0, 0x34, 0xc1, 0x09,
+ 0x00, 0x35, 0x51, 0x01,
+ 0xff, 0xea, 0x52, 0x09,
+ 0x30, 0x34, 0xc5, 0x09,
+ 0x3d, 0xe2, 0xc4, 0x29,
+ 0xb8, 0xe2, 0xc4, 0x19,
+ 0x01, 0xea, 0xc6, 0x01,
+ 0x02, 0xe2, 0xc8, 0x31,
+ 0x02, 0xec, 0x40, 0x31,
+ 0xff, 0xa1, 0xbc, 0x72,
+ 0x02, 0xe8, 0xda, 0x31,
+ 0x02, 0xa0, 0x50, 0x31,
+ 0x00, 0xe2, 0xde, 0x42,
+ 0x80, 0x33, 0x67, 0x02,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0x92, 0x5b,
+ 0x01, 0x33, 0x67, 0x02,
+ 0xe0, 0x36, 0xf9, 0x62,
+ 0x02, 0x33, 0x67, 0x02,
+ 0x20, 0x46, 0xf2, 0x62,
+ 0xff, 0xea, 0x52, 0x09,
+ 0xa8, 0xea, 0x98, 0x5b,
+ 0x04, 0xa8, 0xd9, 0x7a,
+ 0x01, 0x34, 0xc1, 0x31,
+ 0x00, 0xa9, 0xd9, 0x62,
+ 0x01, 0x35, 0xc1, 0x31,
+ 0x00, 0xaa, 0xe3, 0x72,
+ 0x01, 0xa9, 0x52, 0x11,
+ 0xff, 0xa9, 0xce, 0x6a,
+ 0x00, 0xe2, 0xf2, 0x42,
+ 0x10, 0x33, 0x67, 0x02,
+ 0x04, 0xa8, 0xf3, 0x7a,
+ 0xfb, 0xa8, 0x51, 0x0b,
+ 0xff, 0xea, 0x66, 0x0a,
+ 0x01, 0xa4, 0xed, 0x6a,
+ 0x02, 0xa8, 0x90, 0x32,
+ 0x00, 0xe2, 0x56, 0x59,
+ 0x10, 0xa8, 0x9d, 0x7a,
+ 0xff, 0xea, 0xaa, 0x5b,
+ 0x00, 0xe2, 0x9c, 0x42,
+ 0x04, 0xea, 0x3c, 0x59,
+ 0x04, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x9c, 0x42,
+ 0x04, 0xea, 0x3c, 0x59,
+ 0x04, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x08, 0xa8, 0x95, 0x7a,
+ 0xc0, 0x33, 0x09, 0x7b,
+ 0x80, 0x33, 0x95, 0x6a,
+ 0xff, 0x88, 0x09, 0x6b,
+ 0x40, 0x33, 0x95, 0x6a,
+ 0x10, 0xa8, 0x0f, 0x7b,
+ 0x0a, 0xea, 0x3c, 0x59,
+ 0x0a, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x28, 0x5b,
+ 0x00, 0xe2, 0x5c, 0x43,
+ 0x50, 0x4b, 0x16, 0x6b,
+ 0xbf, 0x3a, 0x74, 0x08,
+ 0x01, 0xe0, 0xf8, 0x31,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x01, 0x2e, 0x5d, 0x1a,
+ 0x00, 0x2f, 0x5f, 0x22,
+ 0x04, 0x47, 0x8f, 0x02,
+ 0x01, 0xfc, 0xc0, 0x35,
+ 0x33, 0xea, 0x30, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x02, 0x42, 0x51, 0x31,
+ 0x10, 0xa8, 0x51, 0x03,
+ 0xff, 0x88, 0x37, 0x6b,
+ 0x01, 0xa4, 0x33, 0x6b,
+ 0x02, 0xa4, 0x3b, 0x6b,
+ 0x01, 0x84, 0x3b, 0x7b,
+ 0x02, 0x28, 0x19, 0x33,
+ 0x02, 0xa8, 0x50, 0x36,
+ 0xff, 0x88, 0x3b, 0x73,
+ 0x00, 0xe2, 0x12, 0x5b,
+ 0x02, 0x2c, 0x19, 0x33,
+ 0x02, 0xa8, 0x58, 0x32,
+ 0x04, 0xa4, 0x49, 0x07,
+ 0xc0, 0x33, 0x95, 0x6a,
+ 0x04, 0xa8, 0x51, 0x03,
+ 0x20, 0xa8, 0x5d, 0x6b,
+ 0x02, 0xa8, 0x40, 0x31,
+ 0xc0, 0x34, 0xc1, 0x09,
+ 0x00, 0x35, 0x51, 0x01,
+ 0xff, 0xea, 0x52, 0x09,
+ 0x30, 0x34, 0xc5, 0x09,
+ 0x3d, 0xe2, 0xc4, 0x29,
+ 0xb8, 0xe2, 0xc4, 0x19,
+ 0x01, 0xea, 0xc6, 0x01,
+ 0x02, 0xe2, 0xc8, 0x31,
+ 0x02, 0xa0, 0xda, 0x31,
+ 0x02, 0xa0, 0x50, 0x31,
+ 0xf7, 0x57, 0xae, 0x08,
+ 0x08, 0xea, 0x98, 0x00,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0xee, 0x00, 0x66, 0x6b,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x00, 0xe2, 0x8e, 0x5b,
+ 0x09, 0x4c, 0x68, 0x7b,
+ 0x08, 0x4c, 0x06, 0x68,
+ 0x0b, 0xea, 0x3c, 0x59,
+ 0x0b, 0xea, 0x04, 0x00,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x20, 0x33, 0xdd, 0x79,
+ 0x00, 0xe2, 0x78, 0x5b,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x01, 0x84, 0x7d, 0x7b,
+ 0x01, 0xa4, 0x49, 0x07,
+ 0x08, 0x60, 0x30, 0x33,
+ 0x08, 0x80, 0x41, 0x37,
+ 0xdf, 0x33, 0x67, 0x0a,
+ 0xee, 0x00, 0x8a, 0x6b,
+ 0x05, 0xea, 0xb4, 0x00,
+ 0x33, 0xea, 0x30, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x00, 0xe2, 0x56, 0x59,
+ 0x00, 0xe2, 0x9c, 0x42,
+ 0x01, 0xea, 0x6c, 0x02,
+ 0xc0, 0xea, 0x66, 0x06,
+ 0xff, 0x42, 0x92, 0x7b,
+ 0x04, 0x4c, 0x92, 0x6b,
+ 0xe0, 0x41, 0x6c, 0x0e,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0xff, 0x42, 0x9a, 0x7b,
+ 0x04, 0x4c, 0x9a, 0x6b,
+ 0xe0, 0x41, 0x6c, 0x0a,
+ 0xe0, 0x36, 0xdd, 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, 0xb2, 0x7b,
+ 0xff, 0xea, 0x5a, 0x09,
+ 0xff, 0xea, 0x4c, 0x0d,
+ 0x01, 0xa6, 0xd0, 0x6b,
+ 0x10, 0xad, 0x4e, 0x7d,
+ 0x80, 0xad, 0xc8, 0x6b,
+ 0x08, 0xad, 0x4e, 0x6d,
+ 0x04, 0x84, 0xf9, 0x30,
+ 0x00, 0xea, 0x08, 0x81,
+ 0xff, 0xea, 0xd4, 0x09,
+ 0x02, 0x84, 0xf9, 0x88,
+ 0x1d, 0xea, 0x5a, 0x01,
+ 0x04, 0xa6, 0x4c, 0x05,
+ 0x04, 0xa6, 0x4e, 0x7d,
+ 0xff, 0xea, 0x5a, 0x09,
+ 0x03, 0x84, 0x59, 0x89,
+ 0x03, 0xea, 0x4c, 0x01,
+ 0x80, 0x1a, 0x4e, 0x7d,
+ 0x08, 0xb0, 0xe0, 0x30,
+ 0x04, 0xb0, 0xe0, 0x30,
+ 0x03, 0xb0, 0xf0, 0x30,
+ 0x01, 0x78, 0xdc, 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, 0xf2, 0x7b,
+ 0x04, 0xe2, 0xc4, 0x01,
+ 0x80, 0x83, 0xf9, 0x7b,
+ 0x02, 0xe2, 0xc4, 0x01,
+ 0xff, 0xea, 0x4c, 0x09,
+ 0x01, 0xe2, 0x36, 0x30,
+ 0xc8, 0x19, 0x32, 0x00,
+ 0x88, 0x19, 0x32, 0x00,
+ 0x01, 0xac, 0xd4, 0x99,
+ 0x00, 0xe2, 0x4e, 0x55,
+ 0xfe, 0xa6, 0x4c, 0x0d,
+ 0x0b, 0x98, 0xe1, 0x30,
+ 0x01, 0xa0, 0x4f, 0x09,
+ 0xfd, 0xa4, 0x49, 0x09,
+ 0x80, 0xa3, 0x0f, 0x7c,
+ 0x02, 0xa4, 0x48, 0x01,
+ 0x01, 0xa7, 0x12, 0x7c,
+ 0x04, 0xa4, 0x48, 0x01,
+ 0x01, 0xa4, 0x36, 0x30,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0xfd, 0xa4, 0x49, 0x0b,
+ 0x05, 0xa3, 0x07, 0x33,
+ 0x80, 0x83, 0x1f, 0x6c,
+ 0x02, 0xea, 0x4c, 0x05,
+ 0xff, 0xea, 0x4c, 0x0d,
+ 0x00, 0xe2, 0x28, 0x59,
+ 0x02, 0xa6, 0xb4, 0x6b,
+ 0x80, 0xf9, 0xf2, 0x05,
+ 0xc0, 0x33, 0x2d, 0x7c,
+ 0x03, 0xea, 0x3c, 0x59,
+ 0x03, 0xea, 0x04, 0x00,
+ 0x20, 0x33, 0x51, 0x7c,
+ 0x01, 0x84, 0x37, 0x6c,
+ 0x06, 0xea, 0x3c, 0x59,
+ 0x06, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x54, 0x44,
+ 0x01, 0x00, 0x60, 0x32,
+ 0xee, 0x00, 0x40, 0x6c,
+ 0x05, 0xea, 0xb4, 0x00,
+ 0x33, 0xea, 0x30, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x80, 0x3d, 0x7a, 0x00,
+ 0xfc, 0x42, 0x42, 0x7c,
+ 0x7f, 0x3d, 0x7a, 0x08,
+ 0x00, 0x30, 0x31, 0x59,
+ 0x01, 0x30, 0x01, 0x30,
+ 0x09, 0xea, 0x3c, 0x59,
+ 0x09, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x01, 0xa4, 0x37, 0x6c,
+ 0x00, 0xe2, 0x04, 0x5c,
+ 0x20, 0x33, 0x67, 0x02,
+ 0x01, 0x00, 0x60, 0x32,
+ 0x02, 0xa6, 0x5c, 0x7c,
+ 0x00, 0xe2, 0x20, 0x5c,
+ 0x00, 0xe2, 0x5c, 0x58,
+ 0x00, 0xe2, 0x6c, 0x58,
+ 0x00, 0xe2, 0x30, 0x58,
+ 0x00, 0x30, 0x31, 0x59,
+ 0x01, 0x30, 0x01, 0x30,
+ 0x20, 0x19, 0x5c, 0x6c,
+ 0x00, 0xe2, 0x84, 0x5c,
+ 0x04, 0x19, 0x76, 0x6c,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x01, 0x84, 0x77, 0x7c,
+ 0x01, 0x1b, 0x70, 0x7c,
+ 0x01, 0x1a, 0x76, 0x6c,
+ 0x00, 0xe2, 0x26, 0x44,
+ 0x80, 0x4b, 0x7c, 0x6c,
+ 0x01, 0x4c, 0x78, 0x7c,
+ 0x03, 0x42, 0x26, 0x6c,
+ 0x00, 0xe2, 0xae, 0x5b,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x04, 0x33, 0xdd, 0x79,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x02, 0x1b, 0x8c, 0x7c,
+ 0x08, 0x5d, 0x8a, 0x7c,
+ 0x03, 0x68, 0x00, 0x37,
+ 0x01, 0x84, 0x09, 0x07,
+ 0x08, 0x5d, 0x96, 0x6c,
+ 0x00, 0xe2, 0x5c, 0x58,
+ 0x00, 0x30, 0x31, 0x59,
+ 0x01, 0x30, 0x01, 0x30,
+ 0x00, 0xe2, 0x84, 0x44,
+ 0x80, 0x1b, 0xa0, 0x7c,
+ 0x80, 0x84, 0xa1, 0x6c,
+ 0xff, 0x85, 0x0b, 0x1b,
+ 0xff, 0x86, 0x0d, 0x23,
+ 0xff, 0x87, 0x0f, 0x23,
+ 0xf8, 0x1b, 0x08, 0x0b,
+ 0xff, 0xea, 0x4e, 0x09,
+ 0x04, 0x1b, 0xa8, 0x7c,
+ 0x01, 0xa7, 0x4e, 0x01,
+ 0xff, 0xea, 0x06, 0x0b,
+ 0x03, 0x68, 0x00, 0x37,
+ 0x00, 0xe2, 0xac, 0x58,
+ 0x10, 0xea, 0x18, 0x00,
+ 0xf9, 0xd9, 0xb2, 0x0d,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0xff, 0xea, 0xd4, 0x09,
+ 0x10, 0x5b, 0xcc, 0x6c,
+ 0x08, 0x5b, 0xd4, 0x6c,
+ 0x20, 0x5b, 0xc2, 0x6c,
+ 0x02, 0x5b, 0xe2, 0x6d,
+ 0x0e, 0xea, 0x3c, 0x59,
+ 0x0e, 0xea, 0x04, 0x00,
+ 0x08, 0x19, 0xc8, 0x7c,
+ 0xdf, 0x5c, 0xb8, 0x08,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x01, 0xa4, 0xab, 0x6d,
+ 0x00, 0xe2, 0x04, 0x5c,
+ 0x00, 0xe2, 0xee, 0x5c,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x00, 0xe2, 0x12, 0x5b,
+ 0xf3, 0x92, 0xd5, 0x19,
+ 0x00, 0xe2, 0xe2, 0x54,
+ 0x80, 0x92, 0xe3, 0x6c,
+ 0x0f, 0xea, 0x3c, 0x59,
+ 0x0f, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xea, 0x44,
+ 0x04, 0x8c, 0xe1, 0x30,
+ 0x01, 0xea, 0xf2, 0x00,
+ 0x02, 0xea, 0x36, 0x00,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0x00, 0xe2, 0x70, 0x5d,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x02, 0xa8, 0xf4, 0x31,
+ 0x02, 0xa6, 0x00, 0x7d,
+ 0x00, 0xe2, 0x2a, 0x59,
+ 0x20, 0x5b, 0x0e, 0x6d,
+ 0xfc, 0x42, 0xfa, 0x7c,
+ 0x10, 0x40, 0xfc, 0x6c,
+ 0x20, 0x4d, 0xfe, 0x7c,
+ 0x08, 0x5d, 0x0e, 0x6d,
+ 0x02, 0xa6, 0xb4, 0x6b,
+ 0x00, 0xe2, 0x2a, 0x59,
+ 0x20, 0x5b, 0x0e, 0x6d,
+ 0x01, 0x1b, 0x2e, 0x6d,
+ 0xfc, 0x42, 0x0a, 0x7d,
+ 0x10, 0x40, 0x0c, 0x6d,
+ 0x20, 0x4d, 0x4e, 0x7d,
+ 0x08, 0x5d, 0x4e, 0x7d,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x01, 0x5b, 0x40, 0x31,
+ 0x00, 0xe2, 0x84, 0x5c,
+ 0x00, 0xe2, 0x78, 0x5b,
+ 0x20, 0xea, 0xb6, 0x00,
+ 0x00, 0xe2, 0xae, 0x5b,
+ 0x20, 0x5c, 0xb8, 0x00,
+ 0x04, 0x19, 0x24, 0x6d,
+ 0x01, 0x1a, 0x24, 0x6d,
+ 0x00, 0xe2, 0x2a, 0x59,
+ 0x01, 0x1a, 0x4e, 0x7d,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x20, 0xa0, 0x94, 0x7d,
+ 0x08, 0xa8, 0x2d, 0x7d,
+ 0x00, 0xe2, 0x40, 0x45,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x08, 0xa8, 0x51, 0x7d,
+ 0x04, 0x5d, 0xaa, 0x7d,
+ 0x01, 0x1a, 0xaa, 0x7d,
+ 0x01, 0xa4, 0x49, 0x03,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x02, 0xa8, 0x84, 0x32,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x00, 0xe2, 0x22, 0x43,
+ 0x02, 0xa8, 0x84, 0x32,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0xff, 0xea, 0xd4, 0x19,
+ 0x00, 0xe2, 0x36, 0x59,
+ 0x11, 0x00, 0x00, 0x10,
+ 0x00, 0xe2, 0x80, 0x5d,
+ 0x00, 0xe2, 0x22, 0x53,
+ 0xff, 0xea, 0xd4, 0x0d,
+ 0x00, 0xe2, 0x2a, 0x59,
+ 0x40, 0x5b, 0x5c, 0x6d,
+ 0x04, 0x5d, 0xaa, 0x7d,
+ 0x01, 0x1a, 0xaa, 0x7d,
+ 0x20, 0x4d, 0x4e, 0x7d,
+ 0x40, 0x5b, 0x94, 0x7d,
+ 0x04, 0x5d, 0xaa, 0x7d,
+ 0x01, 0x1a, 0xaa, 0x7d,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x01, 0xa4, 0x49, 0x03,
+ 0x08, 0xa8, 0x41, 0x6d,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0xff, 0x6a, 0x76, 0x7d,
+ 0x10, 0xea, 0x3c, 0x59,
+ 0x10, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x76, 0x45,
+ 0x00, 0xe2, 0x28, 0x59,
+ 0x10, 0x5d, 0x68, 0x6d,
+ 0x40, 0x5b, 0x4e, 0x7d,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0xff, 0xea, 0x10, 0x03,
+ 0x08, 0xa8, 0x51, 0x03,
+ 0x00, 0xe2, 0x40, 0x45,
+ 0x80, 0xf9, 0x4e, 0x6d,
+ 0x01, 0x43, 0xc1, 0x31,
+ 0x00, 0xfb, 0x4e, 0x65,
+ 0x01, 0x42, 0xc1, 0x31,
+ 0x00, 0xfa, 0x4e, 0x65,
+ 0x01, 0xe8, 0xd4, 0x1d,
+ 0x30, 0x3f, 0xc0, 0x09,
+ 0x30, 0xe0, 0x4e, 0x65,
+ 0x40, 0x4b, 0x4e, 0x6d,
+ 0xff, 0xea, 0x52, 0x01,
+ 0xee, 0x00, 0x9a, 0x6d,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x20, 0xea, 0x9a, 0x00,
+ 0xf3, 0x42, 0xa4, 0x6d,
+ 0x12, 0xea, 0x3c, 0x59,
+ 0x12, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x0d, 0xea, 0x3c, 0x59,
+ 0x0d, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xdc, 0x41,
+ 0x11, 0xea, 0x3c, 0x59,
+ 0x11, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x12, 0x5b,
+ 0x08, 0x5a, 0xb4, 0x00,
+ 0x00, 0xe2, 0xcc, 0x5d,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0x00, 0xe2, 0x2a, 0x59,
+ 0x80, 0x1a, 0xbe, 0x7d,
+ 0x00, 0xe2, 0xcc, 0x5d,
+ 0x80, 0x19, 0x32, 0x00,
+ 0x40, 0x5b, 0xc4, 0x6d,
+ 0x08, 0x5a, 0xc4, 0x7d,
+ 0x20, 0x4d, 0x4e, 0x7d,
+ 0x02, 0x84, 0x09, 0x03,
+ 0x40, 0x5b, 0x94, 0x7d,
+ 0x08, 0xa8, 0x39, 0x6d,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0x01, 0x38, 0xe1, 0x30,
+ 0x05, 0x39, 0xe3, 0x98,
+ 0x01, 0xe0, 0xf8, 0x31,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x00, 0x3a, 0xe5, 0x20,
+ 0x00, 0x3b, 0xe7, 0x20,
+ 0x01, 0xfc, 0xc0, 0x31,
+ 0x04, 0xea, 0xe8, 0x30,
+ 0xff, 0xea, 0xf0, 0x08,
+ 0x02, 0xea, 0xf2, 0x00,
+ 0xff, 0xea, 0xf4, 0x0c
+};
+
+typedef int ahd_patch_func_t (struct ahd_softc *ahd);
+static ahd_patch_func_t ahd_patch18_func;
+
+static int
+ahd_patch18_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch17_func;
+
+static int
+ahd_patch17_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0);
+}
+
+static ahd_patch_func_t ahd_patch16_func;
+
+static int
+ahd_patch16_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_INITIATORROLE) != 0);
+}
+
+static ahd_patch_func_t ahd_patch15_func;
+
+static int
+ahd_patch15_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_TARGETROLE) != 0);
+}
+
+static ahd_patch_func_t ahd_patch14_func;
+
+static int
+ahd_patch14_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch13_func;
+
+static int
+ahd_patch13_func(struct ahd_softc *ahd)
+{
+ return ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0);
+}
+
+static ahd_patch_func_t ahd_patch12_func;
+
+static int
+ahd_patch12_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_39BIT_ADDRESSING) != 0);
+}
+
+static ahd_patch_func_t ahd_patch11_func;
+
+static int
+ahd_patch11_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_64BIT_ADDRESSING) != 0);
+}
+
+static ahd_patch_func_t ahd_patch10_func;
+
+static int
+ahd_patch10_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch9_func;
+
+static int
+ahd_patch9_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_BUSFREEREV_BUG) == 0);
+}
+
+static ahd_patch_func_t ahd_patch8_func;
+
+static int
+ahd_patch8_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_ABORT_LQI_BUG) == 0);
+}
+
+static ahd_patch_func_t ahd_patch7_func;
+
+static int
+ahd_patch7_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch6_func;
+
+static int
+ahd_patch6_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch5_func;
+
+static int
+ahd_patch5_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch4_func;
+
+static int
+ahd_patch4_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_NONPACKFIFO_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch3_func;
+
+static int
+ahd_patch3_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch2_func;
+
+static int
+ahd_patch2_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_SET_MODE_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch1_func;
+
+static int
+ahd_patch1_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch0_func;
+
+static int
+ahd_patch0_func(struct ahd_softc *ahd)
+{
+ return (0);
+}
+
+static struct patch {
+ ahd_patch_func_t *patch_func;
+ uint32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
+} patches[] = {
+ { ahd_patch1_func, 0, 3, 3 },
+ { ahd_patch1_func, 1, 1, 2 },
+ { ahd_patch0_func, 2, 1, 1 },
+ { ahd_patch1_func, 3, 3, 3 },
+ { ahd_patch1_func, 4, 1, 2 },
+ { ahd_patch0_func, 5, 1, 1 },
+ { ahd_patch2_func, 6, 1, 2 },
+ { ahd_patch0_func, 7, 1, 1 },
+ { ahd_patch2_func, 24, 1, 2 },
+ { ahd_patch0_func, 25, 1, 1 },
+ { ahd_patch2_func, 35, 1, 2 },
+ { ahd_patch0_func, 36, 1, 1 },
+ { ahd_patch2_func, 39, 1, 2 },
+ { ahd_patch0_func, 40, 1, 1 },
+ { ahd_patch2_func, 43, 1, 2 },
+ { ahd_patch0_func, 44, 1, 1 },
+ { ahd_patch2_func, 46, 1, 2 },
+ { ahd_patch0_func, 47, 1, 1 },
+ { ahd_patch2_func, 50, 1, 2 },
+ { ahd_patch0_func, 51, 1, 1 },
+ { ahd_patch2_func, 54, 1, 2 },
+ { ahd_patch0_func, 55, 1, 1 },
+ { ahd_patch2_func, 152, 6, 1 },
+ { ahd_patch1_func, 158, 2, 1 },
+ { ahd_patch3_func, 160, 1, 1 },
+ { ahd_patch2_func, 169, 1, 2 },
+ { ahd_patch0_func, 170, 1, 1 },
+ { ahd_patch4_func, 171, 2, 2 },
+ { ahd_patch0_func, 173, 6, 3 },
+ { ahd_patch2_func, 176, 1, 2 },
+ { ahd_patch0_func, 177, 1, 1 },
+ { ahd_patch2_func, 180, 1, 2 },
+ { ahd_patch0_func, 181, 1, 1 },
+ { ahd_patch5_func, 183, 2, 1 },
+ { ahd_patch3_func, 191, 16, 2 },
+ { ahd_patch0_func, 207, 1, 1 },
+ { ahd_patch6_func, 227, 2, 1 },
+ { ahd_patch5_func, 231, 2, 1 },
+ { ahd_patch1_func, 245, 1, 2 },
+ { ahd_patch0_func, 246, 1, 1 },
+ { ahd_patch1_func, 249, 1, 2 },
+ { ahd_patch0_func, 250, 1, 1 },
+ { ahd_patch2_func, 253, 1, 2 },
+ { ahd_patch0_func, 254, 1, 1 },
+ { ahd_patch1_func, 309, 1, 2 },
+ { ahd_patch0_func, 310, 1, 1 },
+ { ahd_patch2_func, 318, 1, 2 },
+ { ahd_patch0_func, 319, 1, 1 },
+ { ahd_patch2_func, 322, 1, 2 },
+ { ahd_patch0_func, 323, 1, 1 },
+ { ahd_patch1_func, 330, 1, 2 },
+ { ahd_patch0_func, 331, 1, 1 },
+ { ahd_patch7_func, 350, 1, 1 },
+ { ahd_patch7_func, 353, 1, 1 },
+ { ahd_patch7_func, 355, 1, 1 },
+ { ahd_patch7_func, 367, 1, 1 },
+ { ahd_patch1_func, 377, 1, 2 },
+ { ahd_patch0_func, 378, 1, 1 },
+ { ahd_patch1_func, 380, 1, 2 },
+ { ahd_patch0_func, 381, 1, 1 },
+ { ahd_patch1_func, 389, 1, 2 },
+ { ahd_patch0_func, 390, 1, 1 },
+ { ahd_patch2_func, 401, 1, 2 },
+ { ahd_patch0_func, 402, 1, 1 },
+ { ahd_patch8_func, 404, 1, 1 },
+ { ahd_patch9_func, 431, 1, 1 },
+ { ahd_patch1_func, 438, 1, 2 },
+ { ahd_patch0_func, 439, 1, 1 },
+ { ahd_patch2_func, 451, 1, 2 },
+ { ahd_patch0_func, 452, 1, 1 },
+ { ahd_patch10_func, 480, 1, 1 },
+ { ahd_patch11_func, 489, 1, 2 },
+ { ahd_patch0_func, 490, 1, 1 },
+ { ahd_patch12_func, 495, 1, 1 },
+ { ahd_patch11_func, 496, 1, 1 },
+ { ahd_patch13_func, 509, 1, 2 },
+ { ahd_patch0_func, 510, 1, 1 },
+ { ahd_patch1_func, 532, 1, 2 },
+ { ahd_patch0_func, 533, 1, 1 },
+ { ahd_patch1_func, 536, 1, 2 },
+ { ahd_patch0_func, 537, 1, 1 },
+ { ahd_patch2_func, 542, 1, 2 },
+ { ahd_patch0_func, 543, 1, 1 },
+ { ahd_patch2_func, 547, 1, 2 },
+ { ahd_patch0_func, 548, 1, 1 },
+ { ahd_patch1_func, 549, 1, 2 },
+ { ahd_patch0_func, 550, 1, 1 },
+ { ahd_patch2_func, 561, 1, 2 },
+ { ahd_patch0_func, 562, 1, 1 },
+ { ahd_patch14_func, 566, 1, 1 },
+ { ahd_patch15_func, 571, 1, 1 },
+ { ahd_patch16_func, 572, 2, 1 },
+ { ahd_patch15_func, 576, 1, 2 },
+ { ahd_patch0_func, 577, 1, 1 },
+ { ahd_patch2_func, 584, 1, 2 },
+ { ahd_patch0_func, 585, 1, 1 },
+ { ahd_patch2_func, 600, 1, 2 },
+ { ahd_patch0_func, 601, 1, 1 },
+ { ahd_patch1_func, 607, 1, 2 },
+ { ahd_patch0_func, 608, 1, 1 },
+ { ahd_patch1_func, 622, 1, 2 },
+ { ahd_patch0_func, 623, 1, 1 },
+ { ahd_patch14_func, 647, 1, 1 },
+ { ahd_patch14_func, 663, 1, 1 },
+ { ahd_patch2_func, 675, 1, 2 },
+ { ahd_patch0_func, 676, 1, 1 },
+ { ahd_patch1_func, 693, 1, 2 },
+ { ahd_patch0_func, 694, 1, 1 },
+ { ahd_patch14_func, 699, 1, 1 },
+ { ahd_patch1_func, 719, 1, 2 },
+ { ahd_patch0_func, 720, 1, 1 },
+ { ahd_patch1_func, 722, 1, 2 },
+ { ahd_patch0_func, 723, 1, 1 },
+ { ahd_patch1_func, 725, 1, 2 },
+ { ahd_patch0_func, 726, 1, 1 },
+ { ahd_patch17_func, 728, 1, 2 },
+ { ahd_patch0_func, 729, 2, 1 },
+ { ahd_patch18_func, 732, 4, 2 },
+ { ahd_patch0_func, 736, 1, 1 },
+ { ahd_patch18_func, 742, 11, 1 }
+};
+
+static struct cs {
+ uint16_t begin;
+ uint16_t end;
+} critical_sections[] = {
+ { 11, 12 },
+ { 13, 14 },
+ { 24, 32 },
+ { 33, 46 },
+ { 59, 62 },
+ { 89, 114 },
+ { 115, 146 },
+ { 148, 152 },
+ { 160, 168 },
+ { 191, 224 },
+ { 647, 663 },
+ { 663, 681 },
+ { 686, 692 },
+ { 699, 704 },
+ { 704, 710 }
+};
+
+static const int num_critical_sections = sizeof(critical_sections)
+ / sizeof(*critical_sections);
diff --git a/drivers/scsi/aic7xxx/aic7xxx.h b/drivers/scsi/aic7xxx/aic7xxx.h
index 727170bfec90..6fa90708ad8f 100644
--- a/drivers/scsi/aic7xxx/aic7xxx.h
+++ b/drivers/scsi/aic7xxx/aic7xxx.h
@@ -37,9 +37,9 @@
* 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#34 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.h#66 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.h,v 1.30 2000/11/10 20:13:40 gibbs Exp $
+ * $FreeBSD$
*/
#ifndef _AIC7XXX_H_
@@ -51,6 +51,7 @@
/************************* Forward Declarations *******************************/
struct ahc_platform_data;
struct scb_platform_data;
+struct seeprom_descriptor;
/****************************** Useful Macros *********************************/
#ifndef MAX
@@ -97,6 +98,14 @@ struct scb_platform_data;
(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)))
+#ifdef AHC_DEBUG
+#define SCB_IS_SILENT(scb) \
+ ((ahc_debug & AHC_SHOW_MASKED_ERRORS) == 0 \
+ && (((scb)->flags & SCB_SILENT) != 0))
+#else
+#define SCB_IS_SILENT(scb) \
+ (((scb)->flags & SCB_SILENT) != 0)
+#endif
#define TCL_TARGET_OFFSET(tcl) \
((((tcl) >> 4) & TID) >> 4)
#define TCL_LUN(tcl) \
@@ -170,7 +179,7 @@ struct scb_platform_data;
#define AHC_TMODE_CMDS 256
/* Reset line assertion time in us */
-#define AHC_BUSRESET_DELAY 250
+#define AHC_BUSRESET_DELAY 25
/******************* Chip Characteristics/Operating Settings *****************/
/*
@@ -350,9 +359,11 @@ typedef enum {
*/
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_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 ***************************/
@@ -531,10 +542,27 @@ typedef enum {
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_ABORT = 0x0100,
+ SCB_UNTAGGEDQ = 0x0200,
+ SCB_ACTIVE = 0x0400,
+ SCB_TARGET_IMMEDIATE = 0x0800,
+ SCB_TRANSMISSION_ERROR = 0x1000,/*
+ * 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_TARGET_SCB = 0x2000,
+ SCB_SILENT = 0x4000 /*
+ * Be quiet about transmission type
+ * errors. They are expected and we
+ * don't want to upset the user. This
+ * flag is typically used during DV.
+ */
} scb_flag;
struct scb {
@@ -659,6 +687,11 @@ struct ahc_tmode_lstate;
#define AHC_TRANS_GOAL 0x04 /* Modify negotiation goal */
#define AHC_TRANS_USER 0x08 /* Modify user negotiation settings */
+#define AHC_WIDTH_UNKNOWN 0xFF
+#define AHC_PERIOD_UNKNOWN 0xFF
+#define AHC_OFFSET_UNKNOWN 0x0
+#define AHC_PPR_OPTS_UNKNOWN 0xFF
+
/*
* Transfer Negotiation Information.
*/
@@ -713,10 +746,9 @@ struct ahc_syncrate {
char *rate;
};
-/*
- * The synchronouse transfer rate table.
- */
-extern struct ahc_syncrate ahc_syncrates[];
+/* Safe and valid period for async negotiations. */
+#define AHC_ASYNC_XFER_PERIOD 0x45
+#define AHC_ULTRA2_XFER_PERIOD 0x0a
/*
* Indexes into our table of syncronous transfer rates.
@@ -725,6 +757,7 @@ extern struct ahc_syncrate ahc_syncrates[];
#define AHC_SYNCRATE_ULTRA2 1
#define AHC_SYNCRATE_ULTRA 3
#define AHC_SYNCRATE_FAST 6
+#define AHC_SYNCRATE_MAX AHC_SYNCRATE_DT
/***************************** Lookup Tables **********************************/
/*
@@ -799,7 +832,7 @@ struct seeprom_config {
#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*/
+#define CFENABLEDV 0x4000 /* Perform Domain Validation*/
/*
* Bus Release Time, Host Adapter ID
@@ -866,6 +899,7 @@ struct ahc_suspend_state {
};
typedef void (*ahc_bus_intr_t)(struct ahc_softc *);
+typedef void ahc_callback_t (void *);
struct ahc_softc {
bus_space_tag_t tag;
@@ -941,6 +975,7 @@ struct ahc_softc {
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;
@@ -1017,6 +1052,9 @@ struct ahc_softc {
/* PCI cacheline size. */
u_int pci_cachesize;
+ u_int stack_size;
+ uint16_t *saved_stack;
+
/* Per-Unit descriptive information */
const char *description;
char *name;
@@ -1089,11 +1127,13 @@ void ahc_busy_tcl(struct ahc_softc *ahc,
struct ahc_pci_identity *ahc_find_pci_device(ahc_dev_softc_t);
int ahc_pci_config(struct ahc_softc *,
struct ahc_pci_identity *);
+int ahc_pci_test_register_access(struct ahc_softc *);
/*************************** EISA/VL Front End ********************************/
struct aic7770_identity *aic7770_find_device(uint32_t);
int aic7770_config(struct ahc_softc *ahc,
- struct aic7770_identity *);
+ struct aic7770_identity *,
+ u_int port);
/************************** SCB and SCB queue management **********************/
int ahc_probe_scbs(struct ahc_softc *);
@@ -1116,6 +1156,7 @@ 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);
@@ -1146,6 +1187,11 @@ 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,
@@ -1157,7 +1203,8 @@ 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 scb *scb);
+void ahc_calc_residual(struct ahc_softc *ahc,
+ struct scb *scb);
/*************************** Utility Functions ********************************/
struct ahc_phase_table_entry*
ahc_lookup_phase_entry(int phase);
@@ -1179,11 +1226,20 @@ void ahc_validate_width(struct ahc_softc *ahc,
struct ahc_initiator_tinfo *tinfo,
u_int *bus_width,
role_t role);
+/*
+ * Negotiation types. These are used to qualify if we should renegotiate
+ * even if our goal and current transport parameters are identical.
+ */
+typedef enum {
+ AHC_NEG_TO_GOAL, /* Renegotiate only if goal and curr differ. */
+ AHC_NEG_IF_NON_ASYNC, /* Renegotiate so long as goal is non-async. */
+ AHC_NEG_ALWAYS /* Renegotiat even if goal is async. */
+} ahc_neg_type;
int ahc_update_neg_request(struct ahc_softc*,
struct ahc_devinfo*,
struct ahc_tmode_tstate*,
struct ahc_initiator_tinfo*,
- int /*force*/);
+ ahc_neg_type);
void ahc_set_width(struct ahc_softc *ahc,
struct ahc_devinfo *devinfo,
u_int width, u_int type, int paused);
@@ -1219,6 +1275,35 @@ cam_status ahc_find_tmode_devs(struct ahc_softc *ahc,
#endif
#endif
/******************************* Debug ***************************************/
+#ifdef AHC_DEBUG
+extern uint32_t ahc_debug;
+#define AHC_SHOW_MISC 0x0001
+#define AHC_SHOW_SENSE 0x0002
+#define AHC_DUMP_SEEPROM 0x0004
+#define AHC_SHOW_TERMCTL 0x0008
+#define AHC_SHOW_MEMORY 0x0010
+#define AHC_SHOW_MESSAGES 0x0020
+#define AHC_SHOW_DV 0x0040
+#define AHC_SHOW_SELTO 0x0080
+#define AHC_SHOW_QFULL 0x0200
+#define AHC_SHOW_QUEUE 0x0400
+#define AHC_SHOW_TQIN 0x0800
+#define AHC_SHOW_MASKED_ERRORS 0x1000
+#define AHC_DEBUG_SEQUENCER 0x2000
+#endif
void ahc_print_scb(struct scb *scb);
+void ahc_print_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *dev);
void ahc_dump_card_state(struct ahc_softc *ahc);
+int ahc_print_register(ahc_reg_parse_entry_t *table,
+ u_int num_entries,
+ const char *name,
+ u_int address,
+ u_int value,
+ u_int *cur_column,
+ u_int wrap_point);
+/******************************* 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/drivers/scsi/aic7xxx/aic7xxx.reg b/drivers/scsi/aic7xxx/aic7xxx.reg
index 3ccdd7bb4a44..1744f4f9bb54 100644
--- a/drivers/scsi/aic7xxx/aic7xxx.reg
+++ b/drivers/scsi/aic7xxx/aic7xxx.reg
@@ -37,9 +37,9 @@
* 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.31 2000/11/10 20:13:40 gibbs Exp $
+ * $FreeBSD$
*/
-VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#24 $"
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#36 $"
/*
* This file is processed by the aic7xxx_asm utility for use in assembling
@@ -57,14 +57,14 @@ VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#24 $"
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
+ field TEMODE 0x80
+ field ENSELO 0x40
+ field ENSELI 0x20
+ field ENRSELI 0x10
+ field ENAUTOATNO 0x08
+ field ENAUTOATNI 0x04
+ field ENAUTOATNP 0x02
+ field SCSIRSTO 0x01
}
/*
@@ -74,13 +74,13 @@ register SCSISEQ {
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
+ field DFON 0x80
+ field DFPEXP 0x40
+ field FAST20 0x20
+ field CLRSTCNT 0x10
+ field SPIOEN 0x08
+ field SCAMEN 0x04
+ field CLRCHN 0x02
}
/*
@@ -90,13 +90,13 @@ register SXFRCTL0 {
register SXFRCTL1 {
address 0x002
access_mode RW
- bit BITBUCKET 0x80
- bit SWRAPEN 0x40
- bit ENSPCHK 0x20
+ field BITBUCKET 0x80
+ field SWRAPEN 0x40
+ field ENSPCHK 0x20
mask STIMESEL 0x18
- bit ENSTIMER 0x04
- bit ACTNEGEN 0x02
- bit STPWEN 0x01 /* Powered Termination */
+ field ENSTIMER 0x04
+ field ACTNEGEN 0x02
+ field STPWEN 0x01 /* Powered Termination */
}
/*
@@ -106,14 +106,14 @@ register SXFRCTL1 {
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
+ field CDI 0x80
+ field IOI 0x40
+ field MSGI 0x20
+ field ATNI 0x10
+ field SELI 0x08
+ field BSYI 0x04
+ field REQI 0x02
+ field ACKI 0x01
/*
* Possible phases in SCSISIGI
*/
@@ -137,14 +137,14 @@ register SCSISIGI {
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
+ field CDO 0x80
+ field IOO 0x40
+ field MSGO 0x20
+ field ATNO 0x10
+ field SELO 0x08
+ field BSYO 0x04
+ field REQO 0x02
+ field ACKO 0x01
/*
* Possible phases to write into SCSISIG0
*/
@@ -167,9 +167,9 @@ register SCSISIGO {
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 */
+ field WIDEXFER 0x80 /* Wide transfer control */
+ field ENABLE_CRC 0x40 /* CRC for D-Phases */
+ field SINGLE_EDGE 0x10 /* Disable DT Transfers */
mask SXFR 0x70 /* Sync transfer rate */
mask SXFR_ULTRA2 0x0f /* Sync transfer rate */
mask SOFS 0x0f /* Sync offset */
@@ -185,7 +185,7 @@ register SCSIID {
access_mode RW
mask TID 0xf0 /* Target ID mask */
mask TWIN_TID 0x70
- bit TWIN_CHNLB 0x80
+ field TWIN_CHNLB 0x80
mask OID 0x0f /* Our ID mask */
/*
* SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
@@ -225,18 +225,27 @@ register STCNT {
access_mode RW
}
+/* ALT_MODE registers (Ultra2 and Ultra160 chips) */
+register SXFRCTL2 {
+ address 0x013
+ access_mode RW
+ field AUTORSTDIS 0x10
+ field CMDDMAEN 0x08
+ mask ASYNC_SETUP 0x07
+}
+
/* 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
+ field AUTORATEEN 0x80
+ field AUTOACKEN 0x40
+ field ATNMGMNTEN 0x20
+ field BUSFREEREV 0x10
+ field EXPPHASEDIS 0x08
+ field SCSIDATL_IMGEN 0x04
+ field AUTO_MSGOUT_DE 0x02
+ field DIS_MSGIN_DUALEDGE 0x01
mask OPTIONMODE_DEFAULTS AUTO_MSGOUT_DE|DIS_MSGIN_DUALEDGE
}
@@ -254,12 +263,12 @@ register TARGCRCCNT {
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
+ field CLRSELDO 0x40
+ field CLRSELDI 0x20
+ field CLRSELINGO 0x10
+ field CLRSWRAP 0x08
+ field CLRIOERR 0x08 /* Ultra2 Only */
+ field CLRSPIORDY 0x02
}
/*
@@ -270,15 +279,15 @@ register CLRSINT0 {
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 */
+ field TARGET 0x80 /* Board acting as target */
+ field SELDO 0x40 /* Selection Done */
+ field SELDI 0x20 /* Board has been selected */
+ field SELINGO 0x10 /* Selection In Progress */
+ field SWRAP 0x08 /* 24bit counter wrap */
+ field IOERR 0x08 /* LVD Tranceiver mode changed */
+ field SDONE 0x04 /* STCNT = 0x000000 */
+ field SPIORDY 0x02 /* SCSI PIO Ready */
+ field DMADONE 0x01 /* DMA transfer completed */
}
/*
@@ -288,13 +297,13 @@ register SSTAT0 {
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
+ field CLRSELTIMEO 0x80
+ field CLRATNO 0x40
+ field CLRSCSIRSTI 0x20
+ field CLRBUSFREE 0x08
+ field CLRSCSIPERR 0x04
+ field CLRPHASECHG 0x02
+ field CLRREQINIT 0x01
}
/*
@@ -303,14 +312,14 @@ register CLRSINT1 {
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
+ field SELTO 0x80
+ field ATNTARG 0x40
+ field SCSIRSTI 0x20
+ field PHASEMIS 0x10
+ field BUSFREE 0x08
+ field SCSIPERR 0x04
+ field PHASECHG 0x02
+ field REQINIT 0x01
}
/*
@@ -319,13 +328,13 @@ register SSTAT1 {
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) */
+ field OVERRUN 0x80
+ field SHVALID 0x40 /* Shaddow Layer non-zero */
+ field EXP_ACTIVE 0x10 /* SCSI Expander Active */
+ field CRCVALERR 0x08 /* CRC doesn't match (U3 only) */
+ field CRCENDERR 0x04 /* No terminal CRC packet (U3 only) */
+ field CRCREQERR 0x02 /* Illegal CRC packet req (U3 only) */
+ field DUAL_EDGE_ERR 0x01 /* Incorrect data phase (U3 only) */
mask SFCNT 0x1f
}
@@ -358,14 +367,14 @@ register SCSIID_ULTRA2 {
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
+ field ENSELDO 0x40
+ field ENSELDI 0x20
+ field ENSELINGO 0x10
+ field ENSWRAP 0x08
+ field ENIOERR 0x08 /* LVD Tranceiver mode changes */
+ field ENSDONE 0x04
+ field ENSPIORDY 0x02
+ field ENDMADONE 0x01
}
/*
@@ -376,14 +385,14 @@ register SIMODE0 {
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
+ field ENSELTIMO 0x80
+ field ENATNTARG 0x40
+ field ENSCSIRST 0x20
+ field ENPHASEMIS 0x10
+ field ENBUSFREE 0x08
+ field ENSCSIPERR 0x04
+ field ENPHASECHG 0x02
+ field ENREQINIT 0x01
}
/*
@@ -420,12 +429,12 @@ register SHADDR {
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
+ field STAGE6 0x20
+ field STAGE5 0x10
+ field STAGE4 0x08
+ field STAGE3 0x04
+ field STAGE2 0x02
+ field STAGE1 0x01
alias TARGIDIN
}
@@ -438,16 +447,16 @@ register SELID {
address 0x019
access_mode RW
mask SELID_MASK 0xf0
- bit ONEBIT 0x08
+ field ONEBIT 0x08
}
register SCAMCTL {
address 0x01a
access_mode RW
- bit ENSCAMSELO 0x80
- bit CLRSCAMSELID 0x40
- bit ALTSTIM 0x20
- bit DFLTTID 0x10
+ field ENSCAMSELO 0x80
+ field CLRSCAMSELID 0x40
+ field ALTSTIM 0x20
+ field DFLTTID 0x10
mask SCAMLVL 0x03
}
@@ -471,32 +480,32 @@ register TARGID {
register SPIOCAP {
address 0x01b
access_mode RW
- bit SOFT1 0x80
- bit SOFT0 0x40
- bit SOFTCMDEN 0x20
- bit HAS_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 */
+ field SOFT1 0x80
+ field SOFT0 0x40
+ field SOFTCMDEN 0x20
+ field EXT_BRDCTL 0x10 /* External Board control */
+ field SEEPROM 0x08 /* External serial eeprom logic */
+ field EEPROM 0x04 /* Writable external BIOS ROM */
+ field ROM 0x02 /* Logic for accessing external ROM */
+ field 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
+ field BRDDAT7 0x80
+ field BRDDAT6 0x40
+ field BRDDAT5 0x20
+ field BRDSTB 0x10
+ field BRDCS 0x08
+ field BRDRW 0x04
+ field BRDCTL1 0x02
+ field BRDCTL0 0x01
/* 7890 Definitions */
- bit BRDDAT4 0x10
- bit BRDDAT3 0x08
- bit BRDDAT2 0x04
- bit BRDRW_ULTRA2 0x02
- bit BRDSTB_ULTRA2 0x01
+ field BRDDAT4 0x10
+ field BRDDAT3 0x08
+ field BRDDAT2 0x04
+ field BRDRW_ULTRA2 0x02
+ field BRDSTB_ULTRA2 0x01
}
/*
@@ -525,14 +534,14 @@ register BRDCTL {
*/
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
+ field EXTARBACK 0x80
+ field EXTARBREQ 0x40
+ field SEEMS 0x20
+ field SEERDY 0x10
+ field SEECS 0x08
+ field SEECK 0x04
+ field SEEDO 0x02
+ field SEEDI 0x01
}
/*
* SCSI Block Control (p. 3-32)
@@ -544,14 +553,14 @@ register SEECTL {
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 */
+ field DIAGLEDEN 0x80 /* Aic78X0 only */
+ field DIAGLEDON 0x40 /* Aic78X0 only */
+ field AUTOFLUSHDIS 0x20
+ field SELBUSB 0x08
+ field ENAB40 0x08 /* LVD transceiver active */
+ field ENAB20 0x04 /* SE/HVD transceiver active */
+ field SELWIDE 0x02
+ field XCVR 0x01 /* External transceiver active */
}
/*
@@ -561,14 +570,14 @@ register SBLKCTL {
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
+ field PERRORDIS 0x80
+ field PAUSEDIS 0x40
+ field FAILDIS 0x20
+ field FASTMODE 0x10
+ field BRKADRINTEN 0x08
+ field STEP 0x04
+ field SEQRESET 0x02
+ field LOADRAM 0x01
}
/*
@@ -640,8 +649,8 @@ register NONE {
register FLAGS {
address 0x06b
access_mode RO
- bit ZERO 0x02
- bit CARRY 0x01
+ field ZERO 0x02
+ field CARRY 0x01
}
register SINDIR {
@@ -670,8 +679,8 @@ register STACK {
register BCTL {
address 0x084
access_mode RW
- bit ACE 0x08
- bit ENABLE 0x01
+ field ACE 0x08
+ field ENABLE 0x01
}
/*
@@ -681,23 +690,23 @@ register BCTL {
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 */
+ field CACHETHEN 0x80 /* Cache Threshold enable */
+ field DPARCKEN 0x40 /* Data Parity Check Enable */
+ field MPARCKEN 0x20 /* Memory Parity Check Enable */
+ field 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 */
+ field INTSCBRAMSEL 0x08 /* Internal SCB RAM Select */
+ field RAMPS 0x04 /* External SCB RAM Present */
+ field USCBSIZE32 0x02 /* Use 32byte SCB Page Size */
+ field 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
+ field HADDLDSEL1 0x02 /* Host Address Load Select Bits */
+ field HADDLDSEL0 0x01
}
/*
@@ -747,13 +756,13 @@ const SEQ_MAILBOX_SHIFT 0
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
+ field POWRDN 0x40
+ field SWINT 0x10
+ field IRQMS 0x08
+ field PAUSE 0x04
+ field INTEN 0x02
+ field CHIPRST 0x01
+ field CHIPRSTACK 0x01
}
/*
@@ -789,13 +798,13 @@ register SCBPTR {
register INTSTAT {
address 0x091
access_mode RW
- bit BRKADRINT 0x08
- bit SCSIINT 0x04
- bit CMDCMPLT 0x02
- bit SEQINT 0x01
+ field BRKADRINT 0x08
+ field SCSIINT 0x04
+ field CMDCMPLT 0x02
+ field 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 PROTO_VIOLATION 0x20|SEQINT /* SCSI protocol violation */
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 /*
@@ -858,14 +867,14 @@ register INTSTAT {
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
+ field CIOPARERR 0x80 /* Ultra2 only */
+ field PCIERRSTAT 0x40 /* PCI only */
+ field MPARERR 0x20 /* PCI only */
+ field DPARERR 0x10 /* PCI only */
+ field SQPARERR 0x08
+ field ILLOPCODE 0x04
+ field ILLSADDR 0x02
+ field ILLHADDR 0x01
}
/*
@@ -874,39 +883,39 @@ register ERROR {
register CLRINT {
address 0x092
access_mode WO
- bit CLRPARERR 0x10 /* PCI only */
- bit CLRBRKADRINT 0x08
- bit CLRSCSIINT 0x04
- bit CLRCMDINT 0x02
- bit CLRSEQINT 0x01
+ field CLRPARERR 0x10 /* PCI only */
+ field CLRBRKADRINT 0x08
+ field CLRSCSIINT 0x04
+ field CLRCMDINT 0x02
+ field 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
+ field PRELOADEN 0x80 /* aic7890 only */
+ field WIDEODD 0x40
+ field SCSIEN 0x20
+ field SDMAEN 0x10
+ field SDMAENACK 0x10
+ field HDMAEN 0x08
+ field HDMAENACK 0x08
+ field DIRECTION 0x04
+ field FIFOFLUSH 0x02
+ field 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
+ field PRELOAD_AVAIL 0x80
+ field DFCACHETH 0x40
+ field FIFOQWDEMP 0x20
+ field MREQPEND 0x10
+ field HDONE 0x08
+ field DFTHRESH 0x04
+ field FIFOFULL 0x02
+ field FIFOEMP 0x01
}
register DFWADDR {
@@ -932,7 +941,7 @@ register DFDAT {
register SCBCNT {
address 0x09a
access_mode RW
- bit SCBAUTO 0x80
+ field SCBAUTO 0x80
mask SCBCNT_MASK 0x1f
}
@@ -966,12 +975,12 @@ register QOUTFIFO {
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
+ field CRCONSEEN 0x80
+ field CRCVALCHKEN 0x40
+ field CRCENDCHKEN 0x20
+ field CRCREQCHKEN 0x10
+ field TARGCRCENDEN 0x08
+ field TARGCRCCNTEN 0x04
}
@@ -987,12 +996,12 @@ register QOUTCNT {
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
+ field STATUS_PHASE 0x20
+ field COMMAND_PHASE 0x10
+ field MSG_IN_PHASE 0x08
+ field MSG_OUT_PHASE 0x04
+ field DATA_IN_PHASE 0x02
+ field DATA_OUT_PHASE 0x01
mask DATA_PHASE_MASK 0x03
}
@@ -1002,14 +1011,16 @@ register SCSIPHASE {
register SFUNCT {
address 0x09f
access_mode RW
- bit ALT_MODE 0x80
+ field ALT_MODE 0x80
}
/*
* SCB Definition (p. 5-4)
*/
scb {
- address 0x0a0
+ address 0x0a0
+ size 64
+
SCB_CDB_PTR {
size 4
alias SCB_RESIDUAL_DATACNT
@@ -1039,28 +1050,29 @@ scb {
* the data address.
*/
size 4
- bit SG_LAST_SEG 0x80 /* In the fourth byte */
+ field 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 */
+ field SG_RESID_VALID 0x04 /* In the first byte */
+ field SG_FULL_RESID 0x02 /* In the first byte */
+ field 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
+ field TARGET_SCB 0x80
+ field STATUS_RCVD 0x80
+ field DISCENB 0x40
+ field TAG_ENB 0x20
+ field MK_MESSAGE 0x10
+ field ULTRAENB 0x08
+ field DISCONNECTED 0x04
mask SCB_TAG_TYPE 0x03
}
SCB_SCSIID {
size 1
- bit TWIN_CHNLB 0x80
+ field TWIN_CHNLB 0x80
mask TWIN_TID 0x70
mask TID 0xf0
mask OID 0x0f
@@ -1103,18 +1115,18 @@ const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */
register SEECTL_2840 {
address 0x0c0
access_mode RW
- bit CS_2840 0x04
- bit CK_2840 0x02
- bit DO_2840 0x01
+ field CS_2840 0x04
+ field CK_2840 0x02
+ field DO_2840 0x01
}
register STATUS_2840 {
address 0x0c1
access_mode RW
- bit EEPROM_TF 0x80
+ field EEPROM_TF 0x80
mask BIOS_SEL 0x60
mask ADSEL 0x1e
- bit DI_2840 0x01
+ field DI_2840 0x01
}
/* --------------------- AIC-7870-only definitions -------------------- */
@@ -1138,10 +1150,10 @@ register CCSGADDR {
register CCSGCTL {
address 0x0EB
- bit CCSGDONE 0x80
- bit CCSGEN 0x08
- bit SG_FETCH_NEEDED 0x02 /* Bit used for software state */
- bit CCSGRESET 0x01
+ field CCSGDONE 0x80
+ field CCSGEN 0x08
+ field SG_FETCH_NEEDED 0x02 /* Bit used for software state */
+ field CCSGRESET 0x01
}
register CCSCBCNT {
@@ -1150,12 +1162,12 @@ register CCSCBCNT {
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
+ field CCSCBDONE 0x80
+ field ARRDONE 0x40 /* SCB Array prefetch done */
+ field CCARREN 0x10
+ field CCSCBEN 0x08
+ field CCSCBDIR 0x04
+ field CCSCBRESET 0x01
}
register CCSCBADDR {
@@ -1192,9 +1204,9 @@ register SDSCB_QOFF {
register QOFF_CTLSTA {
address 0x0FA
- bit SCB_AVAIL 0x40
- bit SNSCB_ROLLOVER 0x20
- bit SDSCB_ROLLOVER 0x10
+ field SCB_AVAIL 0x40
+ field SNSCB_ROLLOVER 0x20
+ field SDSCB_ROLLOVER 0x10
mask SCB_QSIZE 0x07
mask SCB_QSIZE_256 0x06
}
@@ -1225,18 +1237,18 @@ 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
+ field ODD_SEG 0x04
+ field LAST_SEG 0x02
+ field 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
+ field ODD_SEG 0x04
+ field LAST_SEG 0x02
+ field LAST_SEG_DONE 0x01
}
/* ---------------------- Scratch RAM Offsets ------------------------- */
/* These offsets are either to values that are initialized by the board's
@@ -1254,7 +1266,8 @@ register SG_CACHE_SHADOW {
*/
scratch_ram {
- address 0x020
+ address 0x020
+ size 58
/*
* 1 byte per target starting at this address for configuration values
@@ -1292,6 +1305,7 @@ scratch_ram {
*/
MWI_RESIDUAL {
size 1
+ alias TARG_IMMEDIATE_SCB
}
/*
* SCBID of the next SCB to be started by the controller.
@@ -1309,28 +1323,29 @@ scratch_ram {
/* 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
- bit FIFOFLUSH 0x02
- bit FIFORESET 0x01
+ field PRELOADEN 0x80
+ field WIDEODD 0x40
+ field SCSIEN 0x20
+ field SDMAEN 0x10
+ field SDMAENACK 0x10
+ field HDMAEN 0x08
+ field HDMAENACK 0x08
+ field DIRECTION 0x04 /* Set indicates PCI->SCSI */
+ field FIFOFLUSH 0x02
+ field FIFORESET 0x01
}
SEQ_FLAGS {
size 1
- bit IDENTIFY_SEEN 0x80
- bit TARGET_CMD_IS_TAGGED 0x40
- bit DPHASE 0x20
+ field NOT_IDENTIFIED 0x80
+ field NO_CDB_SENT 0x40
+ field TARGET_CMD_IS_TAGGED 0x40
+ field 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
+ field TARG_CMD_PENDING 0x10
+ field CMDPHASE_PENDING 0x08
+ field DPHASE_PENDING 0x04
+ field SPHASE_PENDING 0x02
+ field NO_DISCONNECT 0x01
}
/*
* Temporary storage for the
@@ -1348,9 +1363,9 @@ scratch_ram {
*/
LASTPHASE {
size 1
- bit CDI 0x80
- bit IOI 0x40
- bit MSGI 0x20
+ field CDI 0x80
+ field IOI 0x40
+ field MSGI 0x20
mask PHASE_MASK CDI|IOI|MSGI
mask P_DATAOUT 0x00
mask P_DATAIN IOI
@@ -1454,12 +1469,12 @@ scratch_ram {
*/
SCSISEQ_TEMPLATE {
size 1
- bit ENSELO 0x40
- bit ENSELI 0x20
- bit ENRSELI 0x10
- bit ENAUTOATNO 0x08
- bit ENAUTOATNI 0x04
- bit ENAUTOATNP 0x02
+ field ENSELO 0x40
+ field ENSELI 0x20
+ field ENRSELI 0x10
+ field ENAUTOATNO 0x08
+ field ENAUTOATNI 0x04
+ field ENAUTOATNP 0x02
}
/*
@@ -1469,37 +1484,57 @@ scratch_ram {
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.
*/
- INITIATOR_TAG {
- size 1
+ HA_274_BIOSGLOBAL {
+ size 1
+ field HA_274_EXTENDED_TRANS 0x01
+ alias INITIATOR_TAG
}
SEQ_FLAGS2 {
- size 1
- bit SCB_DMA 0x01
- bit TARGET_MSG_PENDING 0x02
+ size 1
+ field SCB_DMA 0x01
+ field TARGET_MSG_PENDING 0x02
}
+}
+
+scratch_ram {
+ address 0x05a
+ size 6
/*
- * These are reserved registers in the card's scratch ram. Some of
- * the values are specified in the AHA2742 technical reference manual
- * and are initialized by the BIOS at boot time.
+ * 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 {
- address 0x05a
size 1
- bit TERM_ENB 0x80
- bit RESET_SCSI 0x40
- bit ENSPCHK 0x20
+ field TERM_ENB 0x80
+ field RESET_SCSI 0x40
+ field 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
+ field EDGE_TRIG 0x80
mask VECTOR 0x0f
}
HOSTCONF {
@@ -1511,13 +1546,18 @@ scratch_ram {
size 1
mask BIOSMODE 0x30
mask BIOSDISABLED 0x30
- bit CHANNEL_B_PRIMARY 0x08
+ field CHANNEL_B_PRIMARY 0x08
}
+}
+
+scratch_ram {
+ address 0x070
+ size 16
+
/*
* Per target SCSI offset values for Ultra2 controllers.
*/
TARG_OFFSET {
- address 0x070
size 16
}
}
@@ -1538,6 +1578,7 @@ const BUS_32_BIT 0x02
const MAX_OFFSET_8BIT 0x0f
const MAX_OFFSET_16BIT 0x08
const MAX_OFFSET_ULTRA2 0x7f
+const MAX_OFFSET 0xff
const HOST_MSG 0xff
/* Target mode command processing constants */
diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq
index 12a57349f6cf..b7846350c3f2 100644
--- a/drivers/scsi/aic7xxx/aic7xxx.seq
+++ b/drivers/scsi/aic7xxx/aic7xxx.seq
@@ -37,10 +37,12 @@
* 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.106 2000/11/12 05:19:46 gibbs Exp $
+ * $FreeBSD$
*/
-VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#37 $"
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#52 $"
+PATCH_ARG_LIST = "struct ahc_softc *ahc"
+PREFIX = "ahc_"
#include "aic7xxx.reg"
#include "scsi_message.h"
@@ -89,7 +91,7 @@ poll_for_work_loop:
test SSTAT0, SELDO|SELDI jnz selection;
test_queue:
/* Has the driver posted any work for us? */
-BEGIN_CRITICAL
+BEGIN_CRITICAL;
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
} else {
@@ -110,7 +112,7 @@ BEGIN_CRITICAL
mov SCBPTR, ARG_1;
}
or SEQ_FLAGS2, SCB_DMA;
-END_CRITICAL
+END_CRITICAL;
dma_queued_scb:
/*
* DMA the SCB from host ram into the current SCB location.
@@ -124,7 +126,7 @@ dma_queued_scb:
* value.
*/
mov A, ARG_1;
-BEGIN_CRITICAL
+BEGIN_CRITICAL;
cmp NEXT_QUEUED_SCB, A jne abort_qinscb;
if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
cmp SCB_TAG, A je . + 2;
@@ -139,7 +141,7 @@ BEGIN_CRITICAL
inc QINPOS;
}
and SEQ_FLAGS2, ~SCB_DMA;
-END_CRITICAL
+END_CRITICAL;
start_waiting:
/*
* Start the first entry on the waiting SCB list.
@@ -304,7 +306,7 @@ ident_messages_done:
} else {
mvi DFDAT, SCB_LIST_NULL;
}
- or SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
+ mvi SEQ_FLAGS, TARG_CMD_PENDING;
test SEQ_FLAGS2, TARGET_MSG_PENDING
jnz target_mesgout_pending;
test SCSISIGI, ATNI jnz target_mesgout_continue;
@@ -388,13 +390,8 @@ initialize_scsiid:
}
/*
- * 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 transfer settings with SCB provided settings.
*/
-initialize_channel:
- or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
set_transfer_settings:
if ((ahc->features & AHC_ULTRA) != 0) {
test SCB_CONTROL, ULTRAENB jz . + 2;
@@ -437,21 +434,29 @@ target_inb:
select_out:
/* Turn off the selection hardware */
and SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
- mvi CLRSINT0, CLRSELDO;
mov SCBPTR, WAITING_SCBH;
mov WAITING_SCBH,SCB_NEXT;
mov SAVED_SCSIID, SCB_SCSIID;
mov SAVED_LUN, SCB_LUN;
- call initialize_channel;
+ call set_transfer_settings;
if ((ahc->flags & AHC_TARGETROLE) != 0) {
test SSTAT0, TARGET jz initiator_select;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+
+ /*
+ * Put tag in connonical location since not
+ * all connections have an SCB.
+ */
+ mov INITIATOR_TAG, SCB_TARGET_ITAG;
+
/*
* 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.
@@ -490,14 +495,16 @@ target_ITloop:
* on the state of NO_DISCONNECT.
*/
test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect;
- mov RETURN_1, ALLZEROS;
+ mvi TARG_IMMEDIATE_SCB, SCB_LIST_NULL;
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;
}
+ cmp TARG_IMMEDIATE_SCB, SCB_LIST_NULL je .;
mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- mov SCB_TAG call dma_scb;
+ mov TARG_IMMEDIATE_SCB call dma_scb;
+ call set_transfer_settings;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
jmp target_synccmd;
target_mesgout:
@@ -634,13 +641,15 @@ complete_target_cmd:
if ((ahc->flags & AHC_INITIATORROLE) != 0) {
initiator_select:
+ or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
/*
* 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 SEQ_FLAGS, NO_CDB_SENT;
+ mvi CLRSINT0, CLRSELDO;
/*
* Main loop for information transfer phases. Wait for the
@@ -698,7 +707,7 @@ clear_target_state:
}
mvi LASTPHASE, P_BUSFREE;
/* clear target specific flags */
- clr SEQ_FLAGS ret;
+ mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;
sg_advance:
clr A; /* add sizeof(struct scatter) */
@@ -812,9 +821,9 @@ calc_mwi_residual_final:
}
p_data:
- test SEQ_FLAGS,IDENTIFY_SEEN jnz p_data_okay;
- mvi NO_IDENT jmp set_seqint;
-p_data_okay:
+ test SEQ_FLAGS,NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed;
+ mvi PROTO_VIOLATION call set_seqint;
+p_data_allowed:
if ((ahc->features & AHC_ULTRA2) != 0) {
mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
} else {
@@ -968,12 +977,12 @@ ultra2_dmafinish:
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;
+ 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;
+ test SSTAT2, SHVALID jnz sgptr_fixup;
call idle_loop;
jmp ultra2_ensure_sg;
@@ -1358,8 +1367,8 @@ 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;
+ test SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay;
+ mvi PROTO_VIOLATION call set_seqint;
p_command_okay:
if ((ahc->features & AHC_ULTRA2) != 0) {
@@ -1391,13 +1400,13 @@ p_command_from_host:
}
mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
}
- jmp p_command_loop;
+ jmp p_command_xfer;
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 memeory.
+ * we aren't going to touch host memory.
*/
clr HADDR[0];
if ((ahc->features & AHC_ULTRA2) != 0) {
@@ -1429,24 +1438,28 @@ p_command_embedded:
call copy_to_fifo_6;
or DFCNTRL, FIFOFLUSH;
}
-p_command_loop:
+p_command_xfer:
+ and SEQ_FLAGS, ~NO_CDB_SENT;
if ((ahc->features & AHC_DT) == 0) {
test SSTAT0, SDONE jnz . + 2;
- test SSTAT1, PHASEMIS jz p_command_loop;
+ test SSTAT1, PHASEMIS jz . - 1;
/*
* 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;
+ test DFCNTRL, SCSIEN jnz .;
}
+ test SSTAT0, SDONE jnz p_command_successful;
+ /*
+ * Don't allow a data phase if the command
+ * was not fully transferred.
+ */
+ or SEQ_FLAGS, NO_CDB_SENT;
+p_command_successful:
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;
/*
@@ -1454,10 +1467,10 @@ p_command_loop:
* and store it into the SCB.
*/
p_status:
- test SEQ_FLAGS,IDENTIFY_SEEN jnz p_status_okay;
- mvi NO_IDENT jmp set_seqint;
+ test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
p_status_okay:
mov SCB_SCSI_STATUS, SCSIDATL;
+ or SCB_CONTROL, STATUS_RCVD;
jmp ITloop;
/*
@@ -1578,13 +1591,15 @@ if ((ahc->features & AHC_WIDE) != 0) {
jmp mesgin_done;
}
+mesgin_proto_violation:
+ mvi PROTO_VIOLATION 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
@@ -1597,27 +1612,49 @@ mesgin_complete:
* 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.
+ /*
+ * 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.
- */
+ /*
+ * If we are identified and have successfully sent the CDB,
+ * any status will do. Optimize this fast path.
+ */
+ test SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation;
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
+
+ /*
+ * 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 jnz mesgin_proto_violation;
+
+ /*
+ * If we recevied good status but never successfully sent the
+ * cdb, abort the command.
+ */
+ test SCB_SCSI_STATUS,0xff jnz complete_accepted;
+ test SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;
+
+complete_accepted:
+ /*
+ * 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
- */
+ /*
+ * 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;
@@ -1670,7 +1707,8 @@ mesgin_disconnect:
* XXX - Wait for more testing.
test SCSISIGI, ATNI jnz mesgin_done;
*/
-
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT
+ jnz mesgin_proto_violation;
or SCB_CONTROL,DISCONNECTED;
if ((ahc->flags & AHC_PAGESCBS) != 0) {
call add_scb_to_disc_list;
@@ -1738,7 +1776,8 @@ mesgin_sdptrs_full:
* 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.
+ * code do the rest. We also reset/reallocate the FIFO to make
+ * sure we have a clean start for the next data or command phase.
*/
mesgin_rdptrs:
and SEQ_FLAGS, ~DPHASE; /*
@@ -1746,6 +1785,7 @@ mesgin_rdptrs:
* the next time through
* the dataphase.
*/
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
jmp mesgin_done;
/*
@@ -1773,7 +1813,7 @@ mesgin_identify:
* 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.
+ * this must be a tagged transaction.
*/
shr SINDEX, 4, SAVED_SCSIID;
and SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
@@ -1894,7 +1934,7 @@ setup_SCB_id_lun_okay:
mov SCBPTR, A;
}
setup_SCB_tagged:
- mvi SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */
+ clr SEQ_FLAGS; /* 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;
@@ -2003,7 +2043,7 @@ target_outb:
* removal of the found SCB from the disconnected list.
*/
if ((ahc->flags & AHC_PAGESCBS) != 0) {
-BEGIN_CRITICAL
+BEGIN_CRITICAL;
findSCB:
mov A, SINDEX; /* Tag passed in SINDEX */
cmp DISCONNECTED_SCBH, SCB_LIST_NULL je findSCB_notFound;
@@ -2025,7 +2065,7 @@ rem_scb_from_disc_list:
mov SCBPTR, SINDEX ret;
rHead:
mov DISCONNECTED_SCBH,SCB_NEXT ret;
-END_CRITICAL
+END_CRITICAL;
findSCB_notFound:
/*
* We didn't find it. Page in the SCB.
@@ -2150,7 +2190,7 @@ set_1byte_addr:
adc DINDIR, A, SINDIR ret;
/*
- * Either post or fetch and SCB from host memory based on the
+ * 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:
@@ -2215,9 +2255,9 @@ dma_scb_fromhost:
* 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.
+ * will drain the FIFO as data are made available.
* When the hang occurs, we know that a multiple
- * of 8 bytes are in the FIFO because the PCI
+ * of 8 bytes is 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.
@@ -2254,7 +2294,6 @@ dma_scb_hang_dma_done:
} 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;
@@ -2308,11 +2347,11 @@ cleanup_scb:
}
add_scb_to_free_list:
if ((ahc->flags & AHC_PAGESCBS) != 0) {
-BEGIN_CRITICAL
+BEGIN_CRITICAL;
mov SCB_NEXT, FREE_SCBH;
mvi SCB_TAG, SCB_LIST_NULL;
mov FREE_SCBH, SCBPTR ret;
-END_CRITICAL
+END_CRITICAL;
} else {
mvi SCB_TAG, SCB_LIST_NULL ret;
}
@@ -2326,7 +2365,7 @@ set_hhaddr:
if ((ahc->flags & AHC_PAGESCBS) != 0) {
get_free_or_disc_scb:
-BEGIN_CRITICAL
+BEGIN_CRITICAL;
cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
return_error:
@@ -2335,14 +2374,14 @@ return_error:
dequeue_disc_scb:
mov SCBPTR, DISCONNECTED_SCBH;
mov DISCONNECTED_SCBH, SCB_NEXT;
-END_CRITICAL
+END_CRITICAL;
mvi DMAPARAMS, FIFORESET;
mov SCB_TAG jmp dma_scb;
-BEGIN_CRITICAL
+BEGIN_CRITICAL;
dequeue_free_scb:
mov SCBPTR, FREE_SCBH;
mov FREE_SCBH, SCB_NEXT ret;
-END_CRITICAL
+END_CRITICAL;
add_scb_to_disc_list:
/*
@@ -2350,10 +2389,10 @@ add_scb_to_disc_list:
* 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
+BEGIN_CRITICAL;
mov SCB_NEXT, DISCONNECTED_SCBH;
mov DISCONNECTED_SCBH, SCBPTR ret;
-END_CRITICAL
+END_CRITICAL;
}
set_seqint:
mov INTSTAT, SINDEX;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_93cx6.c b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
index fa599149a8d5..468d612a44f6 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
@@ -28,9 +28,9 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.c#10 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.c#17 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_93cx6.c,v 1.9 2000/11/10 20:13:41 gibbs Exp $
+ * $FreeBSD$
*/
/*
@@ -67,9 +67,15 @@
*
*/
+#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
@@ -77,9 +83,13 @@
*/
static struct seeprom_cmd {
uint8_t len;
- uint8_t bits[3];
+ 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.
*/
@@ -90,15 +100,55 @@ static struct seeprom_cmd {
(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
-read_seeprom(sd, buf, start_addr, count)
- struct seeprom_descriptor *sd;
- uint16_t *buf;
- u_int start_addr;
- u_int count;
+ahc_read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count)
{
int i = 0;
u_int k = 0;
@@ -110,26 +160,14 @@ read_seeprom(sd, buf, start_addr, count)
* will range from 0 to count-1.
*/
for (k = start_addr; k < count + start_addr; k++) {
- /* 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);
-
/*
* Now we're ready to send the read command followed by the
* address of the 16-bit register we want to read.
*/
- for (i = 0; i < seeprom_read.len; i++) {
- if (seeprom_read.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 (seeprom_read.bits[i] != 0)
- temp ^= sd->sd_DO;
- }
+ 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;
@@ -161,13 +199,7 @@ read_seeprom(sd, buf, start_addr, count)
buf[k - start_addr] = v;
/* Reset the chip select for the next command cycle. */
- 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);
+ reset_seeprom(sd);
}
#ifdef AHC_DUMP_EEPROM
printf("\nSerial EEPROM:\n\t");
@@ -182,8 +214,77 @@ read_seeprom(sd, buf, start_addr, count)
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
-verify_cksum(struct seeprom_config *sc)
+ahc_verify_cksum(struct seeprom_config *sc)
{
int i;
int maxaddr;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_93cx6.h b/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
index ce8e0d2ed20d..859c43ccdd46 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
+++ b/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
@@ -38,9 +38,9 @@
* 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#7 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.h#12 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_93cx6.h,v 1.8 2000/11/10 20:13:41 gibbs Exp $
+ * $FreeBSD$
*/
#ifndef _AIC7XXX_93CX6_H_
#define _AIC7XXX_93CX6_H_
@@ -93,8 +93,10 @@ do { \
#define SEEPROM_DATA_INB(sd) \
ahc_inb(sd->sd_ahc, sd->sd_dataout_offset)
-int read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
- u_int start_addr, u_int count);
-int verify_cksum(struct seeprom_config *sc);
+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/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c
index 35990c38229c..d67bed4e6795 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_core.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_core.c
@@ -1,8 +1,8 @@
/*
* Core routines and tables shareable across OS platforms.
*
- * Copyright (c) 1994-2001 Justin T. Gibbs.
- * Copyright (c) 2000-2001 Adaptec Inc.
+ * Copyright (c) 1994-2002 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,14 +37,20 @@
* 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#50 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#104 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.c,v 1.61 2000/11/13 03:35:43 gibbs Exp $
+ * $FreeBSD$
*/
+#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);
@@ -175,6 +181,7 @@ static void ahc_construct_ppr(struct ahc_softc *ahc,
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_proto_violation(struct ahc_softc *ahc);
static void ahc_handle_message_phase(struct ahc_softc *ahc);
typedef enum {
AHCMSG_1B,
@@ -223,6 +230,7 @@ static int ahc_check_patch(struct ahc_softc *ahc,
u_int start_instr, u_int *skip_addr);
static void ahc_download_instr(struct ahc_softc *ahc,
u_int instrptr, uint8_t *dconsts);
+static int ahc_probe_stack_size(struct ahc_softc *ahc);
#ifdef AHC_TARGET_MODE
static void ahc_queue_lstate_event(struct ahc_softc *ahc,
struct ahc_tmode_lstate *lstate,
@@ -244,10 +252,15 @@ 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
@@ -319,7 +332,7 @@ ahc_run_qoutfifo(struct ahc_softc *ahc)
printf("%s: WARNING no command for scb %d "
"(cmdcmplt)\nQOUTPOS = %d\n",
ahc_name(ahc), scb_index,
- ahc->qoutfifonext - 1);
+ (ahc->qoutfifonext - 1) & 0xFF);
continue;
}
@@ -327,7 +340,7 @@ ahc_run_qoutfifo(struct ahc_softc *ahc)
* Save off the residual
* if there is one.
*/
- ahc_update_residual(scb);
+ ahc_update_residual(ahc, scb);
ahc_done(ahc, scb);
}
}
@@ -426,10 +439,10 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
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 "
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("ahc_intr - referenced scb "
"not valid during seqint 0x%x scb(%d)\n",
- ahc_name(ahc), devinfo.channel,
- devinfo.target, intstat, scb_index);
+ intstat, scb_index);
ahc_dump_card_state(ahc);
panic("for safety");
goto unpause;
@@ -467,7 +480,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
struct ahc_tmode_tstate *tstate;
struct ahc_transinfo *tinfo;
#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWSENSE) {
+ if (ahc_debug & AHC_SHOW_SENSE) {
ahc_print_path(ahc, scb);
printf("SCB %d: requests Check Status\n",
scb->hscb->tag);
@@ -488,9 +501,9 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
/*
* Save off the residual if there is one.
*/
- ahc_update_residual(scb);
+ ahc_update_residual(ahc, scb);
#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWSENSE) {
+ if (ahc_debug & AHC_SHOW_SENSE) {
ahc_print_path(ahc, scb);
printf("Sending Sense\n");
}
@@ -534,7 +547,7 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
== ahc_get_transfer_length(scb)) {
ahc_update_neg_request(ahc, &devinfo,
tstate, targ_info,
- /*force*/TRUE);
+ AHC_NEG_IF_NON_ASYNC);
}
if (tstate->auto_negotiate & devinfo.target_mask) {
hscb->control |= MK_MESSAGE;
@@ -550,16 +563,11 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
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
+ ahc_scb_timer_reset(scb, 5 * 1000000);
break;
}
default:
@@ -613,27 +621,10 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
break;
}
- case NO_IDENT:
+ case PROTO_VIOLATION:
{
- /*
- * 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;
+ ahc_handle_proto_violation(ahc);
+ break;
}
case IGN_WIDE_RES:
ahc_handle_ign_wide_residue(ahc, &devinfo);
@@ -761,7 +752,44 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
ahc_outb(ahc, LASTPHASE, curphase);
ahc_outb(ahc, SCSISIGO, curphase);
}
- ahc_inb(ahc, SCSIDATL);
+ if ((ahc_inb(ahc, SCSISIGI) & (CDI|MSGI)) == 0) {
+ int wait;
+
+ /*
+ * In a data phase. Faster to bitbucket
+ * the data than to individually ack each
+ * byte. This is also the only strategy
+ * that will work with AUTOACK enabled.
+ */
+ ahc_outb(ahc, SXFRCTL1,
+ ahc_inb(ahc, SXFRCTL1) | BITBUCKET);
+ wait = 5000;
+ while (--wait != 0) {
+ if ((ahc_inb(ahc, SCSISIGI)
+ & (CDI|MSGI)) != 0)
+ break;
+ ahc_delay(100);
+ }
+ ahc_outb(ahc, SXFRCTL1,
+ ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+ if (wait == 0) {
+ struct scb *scb;
+ u_int scb_index;
+
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Unable to clear parity error. "
+ "Resetting bus.\n");
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb != NULL)
+ ahc_set_transaction_status(scb,
+ CAM_UNCOR_PARITY);
+ ahc_reset_channel(ahc, devinfo.channel,
+ /*init reset*/TRUE);
+ }
+ } else {
+ ahc_inb(ahc, SCSIDATL);
+ }
}
break;
}
@@ -810,7 +838,12 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
* target does a command complete.
*/
ahc_freeze_devq(ahc, scb);
- ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ 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) {
@@ -926,9 +959,6 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
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';
@@ -957,10 +987,13 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
}
}
+ /* Make sure the sequencer is in a safe location. */
+ ahc_clear_critical_section(ahc);
+
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)
+ && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
scb = NULL;
if ((ahc->features & AHC_ULTRA2) != 0
@@ -1007,6 +1040,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
u_int scsirate;
u_int i;
u_int sstat2;
+ int silent;
lastphase = ahc_inb(ahc, LASTPHASE);
curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
@@ -1034,29 +1068,47 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
break;
}
mesg_out = ahc_phase_table[i].mesg_out;
- if (scb != NULL)
- ahc_print_path(ahc, scb);
- else
+ silent = FALSE;
+ if (scb != NULL) {
+ if (SCB_IS_SILENT(scb))
+ silent = TRUE;
+ else
+ ahc_print_path(ahc, scb);
+ scb->flags |= SCB_TRANSMISSION_ERROR;
+ } 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 (silent == FALSE) {
+ printf("parity error detected %s. "
+ "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
+ ahc_phase_table[i].phasemsg,
+ ahc_inw(ahc, SEQADDR0),
+ 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-");
+ }
+ }
- 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-");
+ if ((ahc->features & AHC_DT) != 0
+ && (sstat2 & DUAL_EDGE_ERR) != 0) {
+ /*
+ * This error applies regardless of
+ * data direction, so ignore the value
+ * in the phase table.
+ */
+ mesg_out = MSG_INITIATOR_DET_ERR;
}
/*
@@ -1112,21 +1164,29 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
printf("%s: ahc_intr - referenced scb not "
"valid during SELTO scb(%d, %d)\n",
ahc_name(ahc), scbptr, scb_index);
+ ahc_dump_card_state(ahc);
} else {
+ /*
+ * 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_set_transaction_status(scb, CAM_SEL_TIMEOUT);
ahc_freeze_devq(ahc, scb);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_SELTO) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("Saw Selection Timeout for SCB 0x%x\n",
+ scb_index);
+ }
+#endif
}
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) {
@@ -1285,13 +1345,19 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
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_clear_msg_state(ahc);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_restart(ahc);
} else {
@@ -1319,7 +1385,7 @@ ahc_force_renegotiation(struct ahc_softc *ahc)
devinfo.target,
&tstate);
ahc_update_neg_request(ahc, &devinfo, tstate,
- targ_info, /*force*/TRUE);
+ targ_info, AHC_NEG_IF_NON_ASYNC);
}
#define AHC_MAX_STEPS 2000
@@ -1382,15 +1448,29 @@ ahc_clear_critical_section(struct ahc_softc *ahc)
simode0 = ahc_inb(ahc, SIMODE0);
ahc_outb(ahc, SIMODE0, 0);
simode1 = ahc_inb(ahc, SIMODE1);
- ahc_outb(ahc, SIMODE1, 0);
+ if ((ahc->features & AHC_DT) != 0)
+ /*
+ * On DT class controllers, we
+ * use the enhanced busfree logic.
+ * Unfortunately we cannot re-enable
+ * busfree detection within the
+ * current connection, so we must
+ * leave it on while single stepping.
+ */
+ ahc_outb(ahc, SIMODE1, ENBUSFREE);
+ else
+ ahc_outb(ahc, SIMODE1, 0);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP);
stepping = TRUE;
}
+ if ((ahc->features & AHC_DT) != 0) {
+ ahc_outb(ahc, CLRSINT1, CLRBUSFREE);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ }
ahc_outb(ahc, HCNTRL, ahc->unpause);
- do {
+ while (!ahc_is_paused(ahc))
ahc_delay(200);
- } while (!ahc_is_paused(ahc));
}
if (stepping) {
ahc_outb(ahc, SIMODE0, simode0);
@@ -1409,11 +1489,18 @@ ahc_clear_intstat(struct ahc_softc *ahc)
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
+uint32_t ahc_debug = AHC_DEBUG_OPTS;
+#endif
+
void
ahc_print_scb(struct scb *scb)
{
@@ -1468,7 +1555,8 @@ ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
&& 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);
+ tstate = (struct ahc_tmode_tstate*)malloc(sizeof(*tstate),
+ M_DEVBUF, M_NOWAIT);
if (tstate == NULL)
return (NULL);
@@ -1482,7 +1570,7 @@ ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
memcpy(tstate, master_tstate, sizeof(*tstate));
memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
tstate->ultraenb = 0;
- for (i = 0; i < 16; i++) {
+ 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,
@@ -1531,7 +1619,8 @@ ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
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) {
+ u_int *period, u_int *ppr_options, role_t role)
+{
struct ahc_transinfo *transinfo;
u_int maxsync;
@@ -1564,6 +1653,10 @@ ahc_devlimited_syncrate(struct ahc_softc *ahc,
else
transinfo = &tinfo->goal;
*ppr_options &= transinfo->ppr_options;
+ if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
+ maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
if (transinfo->period == 0) {
*period = 0;
*ppr_options = 0;
@@ -1740,17 +1833,28 @@ ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
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)
+ struct ahc_initiator_tinfo *tinfo, ahc_neg_type neg_type)
{
u_int auto_negotiate_orig;
auto_negotiate_orig = tstate->auto_negotiate;
+ if (neg_type == AHC_NEG_ALWAYS) {
+ /*
+ * Force our "current" settings to be
+ * unknown so that unless a bus reset
+ * occurs the need to renegotiate is
+ * recorded persistently.
+ */
+ tinfo->curr.period = AHC_PERIOD_UNKNOWN;
+ tinfo->curr.width = AHC_WIDTH_UNKNOWN;
+ tinfo->curr.offset = AHC_OFFSET_UNKNOWN;
+ }
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
+ || (neg_type == AHC_NEG_IF_NON_ASYNC
+ && (tinfo->goal.offset != 0
|| tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
|| tinfo->goal.ppr_options != 0)))
tstate->auto_negotiate |= devinfo->target_mask;
@@ -1881,7 +1985,7 @@ ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
}
update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
- tinfo, /*force*/FALSE);
+ tinfo, AHC_NEG_TO_GOAL);
if (update_needed)
ahc_update_pending_scbs(ahc);
@@ -1943,7 +2047,7 @@ ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
}
update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
- tinfo, /*force*/FALSE);
+ tinfo, AHC_NEG_TO_GOAL);
if (update_needed)
ahc_update_pending_scbs(ahc);
}
@@ -2057,7 +2161,8 @@ ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
if (role == ROLE_TARGET
&& (ahc->features & AHC_MULTI_TID) != 0
- && (ahc_inb(ahc, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
+ && (ahc_inb(ahc, SEQ_FLAGS)
+ & (CMDPHASE_PENDING|TARG_CMD_PENDING|NO_DISCONNECT)) != 0) {
/* We were selected, so pull our id from TARGIDIN */
our_id = ahc_inb(ahc, TARGIDIN) & OID;
} else if ((ahc->features & AHC_ULTRA2) != 0)
@@ -2107,6 +2212,13 @@ ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
devinfo->target_mask = (0x01 << devinfo->target_offset);
}
+void
+ahc_print_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ printf("%s:%c:%d:%d: ", ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+}
+
static void
ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
struct scb *scb)
@@ -2116,7 +2228,7 @@ ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
role = ROLE_INITIATOR;
- if ((scb->hscb->control & TARGET_SCB) != 0)
+ if ((scb->flags & SCB_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);
@@ -2241,7 +2353,6 @@ ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
int dowide;
int dosync;
int doppr;
- int use_ppr;
u_int period;
u_int ppr_options;
u_int offset;
@@ -2263,23 +2374,37 @@ ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
&ppr_options, devinfo->role);
dowide = tinfo->curr.width != tinfo->goal.width;
dosync = tinfo->curr.period != period;
- doppr = tinfo->curr.ppr_options != ppr_options;
+ /*
+ * Only use PPR if we have options that need it, even if the device
+ * claims to support it. There might be an expander in the way
+ * that doesn't.
+ */
+ doppr = ppr_options != 0;
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;
+ dosync = tinfo->goal.offset != 0;
}
if (!dowide && !dosync && !doppr) {
- panic("ahc_intr: AWAITING_MSG for negotiation, "
- "but no negotiation needed\n");
+ /*
+ * Force async with a WDTR message if we have a wide bus,
+ * or just issue an SDTR with a 0 offset.
+ */
+ if ((ahc->features & AHC_WIDE) != 0)
+ dowide = 1;
+ else
+ dosync = 1;
+
+ if (bootverbose) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Ensuring async\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;
+ doppr = 0;
/*
* Both the PPR message and SDTR message require the
@@ -2289,14 +2414,14 @@ ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
* Regardless, guarantee that if we are using WDTR and SDTR
* messages that WDTR comes first.
*/
- if (use_ppr || (dosync && !dowide)) {
+ if (doppr || (dosync && !dowide)) {
offset = tinfo->goal.offset;
ahc_validate_offset(ahc, tinfo, rate, &offset,
- use_ppr ? tinfo->goal.width
- : tinfo->curr.width,
+ doppr ? tinfo->goal.width
+ : tinfo->curr.width,
devinfo->role);
- if (use_ppr) {
+ if (doppr) {
ahc_construct_ppr(ahc, devinfo, period, offset,
tinfo->goal.width, ppr_options);
} else {
@@ -2315,6 +2440,8 @@ static void
ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
u_int period, u_int offset)
{
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
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;
@@ -2357,6 +2484,8 @@ 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)
{
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
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;
@@ -2395,6 +2524,100 @@ ahc_clear_msg_state(struct ahc_softc *ahc)
ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
}
+static void
+ahc_handle_proto_violation(struct ahc_softc *ahc)
+{
+ struct ahc_devinfo devinfo;
+ struct scb *scb;
+ u_int scbid;
+ u_int seq_flags;
+ u_int curphase;
+ u_int lastphase;
+ int found;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+ scbid = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scbid);
+ seq_flags = ahc_inb(ahc, SEQ_FLAGS);
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ 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.
+ */
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Target did not send an IDENTIFY message. "
+ "LASTPHASE = 0x%x.\n", lastphase);
+ scb = NULL;
+ } else if (scb == NULL) {
+ /*
+ * We don't seem to have an SCB active for this
+ * transaction. Print an error and reset the bus.
+ */
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("No SCB found during protocol violation\n");
+ goto proto_violation_reset;
+ } else {
+ ahc_set_transaction_status(scb, CAM_SEQUENCE_FAIL);
+ if ((seq_flags & NO_CDB_SENT) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("No or incomplete CDB sent to device.\n");
+ } else if ((ahc_inb(ahc, 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.
+ */
+ ahc_print_path(ahc, scb);
+ printf("Completed command without status.\n");
+ } else {
+ ahc_print_path(ahc, scb);
+ printf("Unknown protocol violation.\n");
+ ahc_dump_card_state(ahc);
+ }
+ }
+ if ((lastphase & ~P_DATAIN_DT) == 0
+ || lastphase == P_COMMAND) {
+proto_violation_reset:
+ /*
+ * Target either went directly to data/command
+ * phase or didn't respond to our ATN.
+ * The only safe thing to do is to blow
+ * it away with a bus reset.
+ */
+ found = ahc_reset_channel(ahc, 'A', TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahc_name(ahc), 'A', found);
+ } else {
+ /*
+ * Leave the selection hardware off in case
+ * this abort attempt will affect yet to
+ * be sent commands.
+ */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+ ahc_assert_atn(ahc);
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ if (scb == NULL) {
+ ahc_print_devinfo(ahc, &devinfo);
+ ahc->msgout_buf[0] = MSG_ABORT_TASK;
+ ahc->msgout_len = 1;
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ } else {
+ ahc_print_path(ahc, scb);
+ scb->flags |= SCB_ABORT;
+ }
+ printf("Protocol violation %s. Attempting to abort.\n",
+ ahc_lookup_phase_entry(curphase)->phasemsg);
+ }
+}
+
/*
* Manual message loop handler.
*/
@@ -2420,8 +2643,21 @@ reswitch:
if (ahc->msgout_len == 0)
panic("HOST_MSG_LOOP interrupt with no active message");
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("INITIATOR_MSG_OUT");
+ }
+#endif
phasemis = bus_phase != P_MESGOUT;
if (phasemis) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahc_lookup_phase_entry(bus_phase)
+ ->phasemsg);
+ }
+#endif
if (bus_phase == P_MESGIN) {
/*
* Change gears and see if
@@ -2442,6 +2678,10 @@ reswitch:
if (ahc->send_msg_perror) {
ahc_outb(ahc, CLRSINT1, CLRATNO);
ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n", ahc->send_msg_perror);
+#endif
ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
break;
}
@@ -2468,6 +2708,11 @@ reswitch:
* the next byte on the bus.
*/
ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahc->msgout_buf[ahc->msgout_index]);
+#endif
ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
break;
}
@@ -2476,9 +2721,21 @@ reswitch:
int phasemis;
int message_done;
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("INITIATOR_MSG_IN");
+ }
+#endif
phasemis = bus_phase != P_MESGIN;
-
if (phasemis) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahc_lookup_phase_entry(bus_phase)
+ ->phasemsg);
+ }
+#endif
ahc->msgin_index = 0;
if (bus_phase == P_MESGOUT
&& (ahc->send_msg_perror == TRUE
@@ -2493,6 +2750,11 @@ reswitch:
/* Pull the byte in without acking it */
ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahc->msgin_buf[ahc->msgin_index]);
+#endif
message_done = ahc_parse_msg(ahc, &devinfo);
@@ -2508,14 +2770,22 @@ reswitch:
* assert ATN so the target takes us to the
* message out phase.
*/
- if (ahc->msgout_len != 0)
- ahc_assert_atn(ahc);
+ if (ahc->msgout_len != 0) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf("Asserting ATN for response\n");
+#endif
+ }
} else
ahc->msgin_index++;
- /* Ack the byte */
- ahc_outb(ahc, CLRSINT1, CLRREQINIT);
- ahc_inb(ahc, SCSIDATL);
+ 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:
@@ -2726,6 +2996,17 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
* 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 */
@@ -2895,7 +3176,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
AHC_TRANS_ACTIVE, /*paused*/TRUE);
if (sending_reply == FALSE && reject == FALSE) {
- if (tinfo->goal.period) {
+ if (tinfo->goal.offset) {
ahc->msgout_index = 0;
ahc->msgout_len = 0;
ahc_build_transfer_msg(ahc, devinfo);
@@ -3160,7 +3441,7 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
* but rejected our response, we already cleared the
* sync rate before sending our WDTR.
*/
- if (tinfo->goal.period) {
+ if (tinfo->goal.offset) {
/* Start the sync negotiation */
ahc->msgout_index = 0;
@@ -3515,6 +3796,15 @@ ahc_alloc(void *platform_arg, char *name)
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;
@@ -3527,7 +3817,7 @@ ahc_alloc(void *platform_arg, char *name)
ahc->bugs = AHC_BUGNONE;
ahc->flags = AHC_FNONE;
- for (i = 0; i < 16; i++)
+ 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);
@@ -3614,6 +3904,22 @@ ahc_softc_insert(struct ahc_softc *ahc)
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)
{
@@ -3694,6 +4000,10 @@ ahc_free(struct ahc_softc *ahc)
#endif
if (ahc->name != NULL)
free(ahc->name, M_DEVBUF);
+ if (ahc->seep_config != NULL)
+ free(ahc->seep_config, M_DEVBUF);
+ if (ahc->saved_stack != NULL)
+ free(ahc->saved_stack, M_DEVBUF);
#ifndef __FreeBSD__
free(ahc, M_DEVBUF);
#endif
@@ -3714,7 +4024,7 @@ ahc_shutdown(void *arg)
ahc_outb(ahc, SXFRCTL0, 0);
ahc_outb(ahc, DSPCISTATUS, 0);
- for (i = TARG_SCSIRATE; i < HA_274_BIOSCTRL; i++)
+ for (i = TARG_SCSIRATE; i < SCSICONF; i++)
ahc_outb(ahc, i, 0);
}
@@ -3753,7 +4063,10 @@ ahc_reset(struct ahc_softc *ahc)
ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
/*
- * Ensure that the reset has finished
+ * 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 {
@@ -3846,11 +4159,26 @@ ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
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);
@@ -3860,17 +4188,15 @@ ahc_build_free_scb_list(struct ahc_softc *ahc)
else
ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
- /* Make the tag number invalid */
+ /* 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);
-
- /* Ensure we clear the 0 SCB's control byte. */
- ahc_outb(ahc, SCBPTR, 0);
- ahc_outb(ahc, SCB_CONTROL, 0);
}
static int
@@ -3982,7 +4308,7 @@ ahc_init_scbdata(struct ahc_softc *ahc)
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,
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/8,
/*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
@@ -4213,6 +4539,12 @@ ahc_init(struct ahc_softc *ahc)
size_t driver_data_size;
uint32_t physaddr;
+ ahc->stack_size = ahc_probe_stack_size(ahc);
+ ahc->saved_stack = malloc(ahc->stack_size * sizeof(uint16_t),
+ M_DEVBUF, M_NOWAIT);
+ if (ahc->saved_stack == NULL)
+ return (ENOMEM);
+
#ifdef AHC_DEBUG_SEQUENCER
ahc->flags |= AHC_SEQUENCER_DEBUG;
#endif
@@ -4234,6 +4566,12 @@ ahc_init(struct ahc_softc *ahc)
}
}
printf ("\n");
+ /*
+ * Reading uninitialized scratch ram may
+ * generate parity errors.
+ */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
#endif
max_targ = 15;
@@ -4364,7 +4702,7 @@ ahc_init(struct ahc_softc *ahc)
}
#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWMISC) {
+ if (ahc_debug & AHC_SHOW_MISC) {
printf("%s: hardware scb %d bytes; kernel scb %d bytes; "
"ahc_dma %d bytes\n",
ahc_name(ahc),
@@ -4425,7 +4763,7 @@ ahc_init(struct ahc_softc *ahc)
tagenable = ALL_TARGETS_MASK;
/* Grab the disconnection disable table and invert it for our needs */
- if (ahc->flags & AHC_USEDEFAULTS) {
+ 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|
@@ -4699,21 +5037,28 @@ ahc_pause_and_flushwork(struct ahc_softc *ahc)
{
int intstat;
int maxloops;
+ int paused;
maxloops = 1000;
ahc->flags |= AHC_ALL_INTERRUPTS;
intstat = 0;
+ paused = FALSE;
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);
if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
break;
- maxloops--;
- } while (((intstat = ahc_inb(ahc, INTSTAT)) & INT_PEND) && --maxloops);
+ } while (--maxloops
+ && (((intstat = ahc_inb(ahc, INTSTAT)) & INT_PEND) != 0
+ || (ahc_inb(ahc, SSTAT0) & (SELDO|SELINGO))));
if (maxloops == 0) {
printf("Infinite interrupt loop, INTSTAT = %x",
- ahc_inb(ahc, INTSTAT));
+ ahc_inb(ahc, INTSTAT));
}
ahc_platform_flushwork(ahc);
ahc->flags &= ~AHC_ALL_INTERRUPTS;
@@ -5046,8 +5391,8 @@ ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
static int
ahc_qinfifo_count(struct ahc_softc *ahc)
{
- u_int8_t qinpos;
- u_int8_t diff;
+ uint8_t qinpos;
+ uint8_t diff;
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
qinpos = ahc_inb(ahc, SNSCB_QOFF);
@@ -5068,11 +5413,10 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
uint8_t qinstart;
uint8_t qinpos;
uint8_t qintail;
- uint8_t next, prev;
+ uint8_t next;
+ uint8_t prev;
uint8_t curscbptr;
int found;
- int maxtarget;
- int i;
int have_qregs;
qintail = ahc->qinfifonext;
@@ -5083,7 +5427,6 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
} else
qinstart = ahc_inb(ahc, QINPOS);
qinpos = qinstart;
- next = ahc_inb(ahc, NEXT_QUEUED_SCB);
found = 0;
prev_scb = NULL;
@@ -5123,8 +5466,7 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
ostat = ahc_get_transaction_status(scb);
if (ostat == CAM_REQ_INPROG)
- ahc_set_transaction_status(scb,
- status);
+ ahc_set_transaction_status(scb, status);
cstat = ahc_get_transaction_status(scb);
if (cstat != CAM_REQ_CMP)
ahc_freeze_scb(scb);
@@ -5133,9 +5475,9 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
ahc_done(ahc, scb);
/* FALLTHROUGH */
+ }
case SEARCH_REMOVE:
break;
- }
case SEARCH_COUNT:
ahc_qinfifo_requeue(ahc, prev_scb, scb);
prev_scb = scb;
@@ -5262,9 +5604,33 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
}
ahc_outb(ahc, SCBPTR, curscbptr);
- /*
- * And lastly, the untagged holding queues.
- */
+ 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) {
@@ -5303,37 +5669,38 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
if ((scb->flags & SCB_ACTIVE) != 0)
continue;
- 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 untaggedQ\n");
- ahc_done(ahc, scb);
- break;
- }
- case SEARCH_REMOVE:
- TAILQ_REMOVE(untagged_q, scb,
- links.tqe);
- break;
- case SEARCH_COUNT:
- break;
- }
+ 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:
+ scb->flags &= ~SCB_UNTAGGEDQ;
+ TAILQ_REMOVE(untagged_q, scb, links.tqe);
+ break;
+ case SEARCH_COUNT:
+ break;
}
}
}
@@ -5646,6 +6013,7 @@ ahc_reset_current_bus(struct ahc_softc *ahc)
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);
@@ -5687,6 +6055,16 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
*/
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);
}
@@ -5708,10 +6086,6 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
*/
ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
- ahc_outb(ahc, SIMODE1, simode1);
- if (initiate_reset)
- ahc_reset_current_bus(ahc);
- ahc_clear_intstat(ahc);
#if AHC_TARGET_MODE
/*
* Bus resets clear ENSELI, so we cannot
@@ -5719,19 +6093,18 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
* if we are in target mode.
*/
if ((ahc->flags & AHC_TARGETROLE) != 0)
- ahc_outb(ahc, SIMODE1, simode1 | ENSCSIRST);
+ 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 */
- ahc_clear_msg_state(ahc);
simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
- ahc_outb(ahc, SIMODE1, simode1);
- if (initiate_reset)
- ahc_reset_current_bus(ahc);
- ahc_clear_intstat(ahc);
#if AHC_TARGET_MODE
/*
* Bus resets clear ENSELI, so we cannot
@@ -5739,8 +6112,12 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
* if we are in target mode.
*/
if ((ahc->flags & AHC_TARGETROLE) != 0)
- ahc_outb(ahc, SIMODE1, simode1 | ENSCSIRST);
+ 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;
}
@@ -5819,7 +6196,7 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
* Calculate the residual for a just completed SCB.
*/
void
-ahc_calc_residual(struct scb *scb)
+ahc_calc_residual(struct ahc_softc *ahc, struct scb *scb)
{
struct hardware_scb *hscb;
struct status_pkt *spkt;
@@ -5893,7 +6270,7 @@ ahc_calc_residual(struct scb *scb)
ahc_set_sense_residual(scb, resid);
#ifdef AHC_DEBUG
- if ((ahc_debug & AHC_SHOWMISC) != 0) {
+ if ((ahc_debug & AHC_SHOW_MISC) != 0) {
ahc_print_path(ahc, scb);
printf("Handled Residual of %d bytes\n", resid);
}
@@ -6275,14 +6652,98 @@ ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
}
}
+static int
+ahc_probe_stack_size(struct ahc_softc *ahc)
+{
+ int last_probe;
+
+ last_probe = 0;
+ while (1) {
+ int i;
+
+ /*
+ * We avoid using 0 as a pattern to avoid
+ * confusion if the stack implementation
+ * "back-fills" with zeros when "poping'
+ * entries.
+ */
+ for (i = 1; i <= last_probe+1; i++) {
+ ahc_outb(ahc, STACK, i & 0xFF);
+ ahc_outb(ahc, STACK, (i >> 8) & 0xFF);
+ }
+
+ /* Verify */
+ for (i = last_probe+1; i > 0; i--) {
+ u_int stack_entry;
+
+ stack_entry = ahc_inb(ahc, STACK)
+ |(ahc_inb(ahc, STACK) << 8);
+ if (stack_entry != i)
+ goto sized;
+ }
+ last_probe++;
+ }
+sized:
+ return (last_probe);
+}
+
+int
+ahc_print_register(ahc_reg_parse_entry_t *table, u_int num_entries,
+ const char *name, u_int address, u_int value,
+ u_int *cur_column, u_int wrap_point)
+{
+ int printed;
+ u_int printed_mask;
+
+ if (cur_column != NULL && *cur_column >= wrap_point) {
+ printf("\n");
+ *cur_column = 0;
+ }
+ printed = printf("%s[0x%x]", name, value);
+ if (table == NULL) {
+ printed += printf(" ");
+ *cur_column += printed;
+ return (printed);
+ }
+ printed_mask = 0;
+ while (printed_mask != 0xFF) {
+ int entry;
+
+ for (entry = 0; entry < num_entries; entry++) {
+ if (((value & table[entry].mask)
+ != table[entry].value)
+ || ((printed_mask & table[entry].mask)
+ == table[entry].mask))
+ continue;
+
+ printed += printf("%s%s",
+ printed_mask == 0 ? ":(" : "|",
+ table[entry].name);
+ printed_mask |= table[entry].mask;
+
+ break;
+ }
+ if (entry >= num_entries)
+ break;
+ }
+ if (printed_mask != 0)
+ printed += printf(") ");
+ else
+ printed += printf(" ");
+ if (cur_column != NULL)
+ *cur_column += printed;
+ return (printed);
+}
+
void
ahc_dump_card_state(struct ahc_softc *ahc)
{
- struct scb *scb;
- struct scb_tailq *untagged_q;
- int target;
- int maxtarget;
- int i;
+ struct scb *scb;
+ struct scb_tailq *untagged_q;
+ u_int cur_col;
+ int target;
+ int maxtarget;
+ int i;
uint8_t last_phase;
uint8_t qinpos;
uint8_t qintail;
@@ -6299,23 +6760,36 @@ ahc_dump_card_state(struct ahc_softc *ahc)
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\n", ahc_inb(ahc, HCNT));
- 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));
+ printf("HCNT = 0x%x SCBPTR = 0x%x\n", ahc_inb(ahc, HCNT),
+ ahc_inb(ahc, SCBPTR));
+ cur_col = 0;
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);
+ ahc_scsisigi_print(ahc_inb(ahc, SCSISIGI), &cur_col, 50);
+ ahc_scsiphase_print(ahc_inb(ahc, SCSIPHASE), &cur_col, 50);
+ ahc_scsibusl_print(ahc_inb(ahc, SCSIBUSL), &cur_col, 50);
+ ahc_lastphase_print(ahc_inb(ahc, LASTPHASE), &cur_col, 50);
+ ahc_scsiseq_print(ahc_inb(ahc, SCSISEQ), &cur_col, 50);
+ ahc_sblkctl_print(ahc_inb(ahc, SBLKCTL), &cur_col, 50);
+ ahc_seqctl_print(ahc_inb(ahc, SEQCTL), &cur_col, 50);
+ ahc_seq_flags_print(ahc_inb(ahc, SEQ_FLAGS), &cur_col, 50);
+ ahc_sstat0_print(ahc_inb(ahc, SSTAT0), &cur_col, 50);
+ ahc_sstat1_print(ahc_inb(ahc, SSTAT1), &cur_col, 50);
+ ahc_sstat2_print(ahc_inb(ahc, SSTAT2), &cur_col, 50);
+ ahc_sstat3_print(ahc_inb(ahc, SSTAT3), &cur_col, 50);
+ ahc_simode0_print(ahc_inb(ahc, SIMODE0), &cur_col, 50);
+ ahc_simode1_print(ahc_inb(ahc, SIMODE1), &cur_col, 50);
+ ahc_sxfrctl0_print(ahc_inb(ahc, SXFRCTL0), &cur_col, 50);
+ ahc_dfcntrl_print(ahc_inb(ahc, DFCNTRL), &cur_col, 50);
+ ahc_dfstatus_print(ahc_inb(ahc, DFSTATUS), &cur_col, 50);
+ if (cur_col != 0)
+ printf("\n");
+ printf("STACK:");
+ for (i = 0; i < ahc->stack_size; i++) {
+ ahc->saved_stack[i] =
+ ahc_inb(ahc, STACK)|(ahc_inb(ahc, STACK) << 8);
+ printf(" 0x%x", ahc->saved_stack[i]);
+ }
+ printf("\nSCB 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 */
@@ -6372,18 +6846,34 @@ ahc_dump_card_state(struct ahc_softc *ahc)
}
printf("\n");
+ printf("Sequencer SCB Info: ");
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ ahc_outb(ahc, SCBPTR, i);
+ cur_col = printf("\n%3d ", i);
+
+ ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL), &cur_col, 60);
+ ahc_scb_scsiid_print(ahc_inb(ahc, SCB_SCSIID), &cur_col, 60);
+ ahc_scb_lun_print(ahc_inb(ahc, SCB_LUN), &cur_col, 60);
+ ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
+ }
+ 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", scb->hscb->tag);
+ cur_col = printf("\n%3d ", scb->hscb->tag);
+ ahc_scb_control_print(scb->hscb->control, &cur_col, 60);
+ ahc_scb_scsiid_print(scb->hscb->scsiid, &cur_col, 60);
+ ahc_scb_lun_print(scb->hscb->lun, &cur_col, 60);
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("(");
+ ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL),
+ &cur_col, 60);
+ ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
+ printf(")");
}
}
printf("\n");
@@ -6469,6 +6959,7 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
u_int target;
u_int lun;
u_int target_mask;
+ u_int our_id;
u_long s;
char channel;
@@ -6480,15 +6971,33 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
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 (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) {
+ /*
+ * our_id represents our initiator ID, or
+ * the ID of the first target to have an
+ * enabled lun in target mode. There are
+ * two cases that may preclude enabling a
+ * target id other than our_id.
+ *
+ * o our_id is for an active initiator role.
+ * Since the hardware does not support
+ * reselections to the initiator role at
+ * anything other than our_id, and our_id
+ * is used by the hardware to indicate the
+ * ID to use for both select-out and
+ * reselect-out operations, the only target
+ * ID we can support in this mode is our_id.
+ *
+ * o The MULTARGID feature is not available and
+ * a previous target mode ID has been enabled.
+ */
+ if ((ahc->features & AHC_MULTIROLE) != 0) {
- if (ccb->ccb_h.target_id != our_id) {
if ((ahc->features & AHC_MULTI_TID) != 0
&& (ahc->flags & AHC_INITIATORROLE) != 0) {
/*
@@ -6510,6 +7019,10 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
*/
status = CAM_TID_INVALID;
}
+ } else if ((ahc->features & AHC_MULTI_TID) == 0
+ && ahc->enabled_luns > 0) {
+
+ status = CAM_TID_INVALID;
}
}
@@ -6907,6 +7420,8 @@ ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
/*
* 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;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_inline.h b/drivers/scsi/aic7xxx/aic7xxx_inline.h
index 617aaf957016..53de3904b10b 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_inline.h
+++ b/drivers/scsi/aic7xxx/aic7xxx_inline.h
@@ -37,9 +37,9 @@
* 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#31 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_inline.h#39 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_inline.h,v 1.8 2000/11/12 05:19:46 gibbs Exp $
+ * $FreeBSD$
*/
#ifndef _AIC7XXX_INLINE_H_
@@ -231,12 +231,25 @@ ahc_name(struct ahc_softc *ahc)
/*********************** Miscelaneous Support Functions ***********************/
-static __inline void ahc_update_residual(struct scb *scb);
+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 uint16_t
+ ahc_inw(struct ahc_softc *ahc, u_int port);
+static __inline void ahc_outw(struct ahc_softc *ahc, u_int port,
+ u_int value);
+static __inline uint32_t
+ ahc_inl(struct ahc_softc *ahc, u_int port);
+static __inline void ahc_outl(struct ahc_softc *ahc, u_int port,
+ uint32_t value);
+static __inline uint64_t
+ ahc_inq(struct ahc_softc *ahc, u_int port);
+static __inline void ahc_outq(struct ahc_softc *ahc, u_int port,
+ uint64_t value);
static __inline struct scb*
ahc_get_scb(struct ahc_softc *ahc);
static __inline void ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
@@ -255,13 +268,13 @@ static __inline uint32_t
* for this SCB/transaction.
*/
static __inline void
-ahc_update_residual(struct scb *scb)
+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(scb);
+ ahc_calc_residual(ahc, scb);
}
/*
@@ -284,6 +297,63 @@ ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
return (&(*tstate)->transinfo[remote_id]);
}
+static __inline uint16_t
+ahc_inw(struct ahc_softc *ahc, u_int port)
+{
+ return ((ahc_inb(ahc, port+1) << 8) | ahc_inb(ahc, port));
+}
+
+static __inline void
+ahc_outw(struct ahc_softc *ahc, u_int port, u_int value)
+{
+ ahc_outb(ahc, port, value & 0xFF);
+ ahc_outb(ahc, port+1, (value >> 8) & 0xFF);
+}
+
+static __inline uint32_t
+ahc_inl(struct ahc_softc *ahc, u_int port)
+{
+ return ((ahc_inb(ahc, port))
+ | (ahc_inb(ahc, port+1) << 8)
+ | (ahc_inb(ahc, port+2) << 16)
+ | (ahc_inb(ahc, port+3) << 24));
+}
+
+static __inline void
+ahc_outl(struct ahc_softc *ahc, u_int port, uint32_t value)
+{
+ ahc_outb(ahc, port, (value) & 0xFF);
+ ahc_outb(ahc, port+1, ((value) >> 8) & 0xFF);
+ ahc_outb(ahc, port+2, ((value) >> 16) & 0xFF);
+ ahc_outb(ahc, port+3, ((value) >> 24) & 0xFF);
+}
+
+static __inline uint64_t
+ahc_inq(struct ahc_softc *ahc, u_int port)
+{
+ return ((ahc_inb(ahc, port))
+ | (ahc_inb(ahc, port+1) << 8)
+ | (ahc_inb(ahc, port+2) << 16)
+ | (ahc_inb(ahc, port+3) << 24)
+ | (((uint64_t)ahc_inb(ahc, port+4)) << 32)
+ | (((uint64_t)ahc_inb(ahc, port+5)) << 40)
+ | (((uint64_t)ahc_inb(ahc, port+6)) << 48)
+ | (((uint64_t)ahc_inb(ahc, port+7)) << 56));
+}
+
+static __inline void
+ahc_outq(struct ahc_softc *ahc, u_int port, uint64_t value)
+{
+ ahc_outb(ahc, port, value & 0xFF);
+ ahc_outb(ahc, port+1, (value >> 8) & 0xFF);
+ ahc_outb(ahc, port+2, (value >> 16) & 0xFF);
+ ahc_outb(ahc, port+3, (value >> 24) & 0xFF);
+ ahc_outb(ahc, port+4, (value >> 32) & 0xFF);
+ ahc_outb(ahc, port+5, (value >> 40) & 0xFF);
+ ahc_outb(ahc, port+6, (value >> 48) & 0xFF);
+ ahc_outb(ahc, port+7, (value >> 56) & 0xFF);
+}
+
/*
* Get a free scb. If there are none, see if we can allocate a new SCB.
*/
@@ -357,8 +427,8 @@ ahc_swap_with_next_hscb(struct ahc_softc *ahc, struct scb *scb)
memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
if ((scb->flags & SCB_CDB32_PTR) != 0) {
q_hscb->shared_data.cdb_ptr =
- ahc_hscb_busaddr(ahc, q_hscb->tag)
- + offsetof(struct hardware_scb, cdb32);
+ 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;
@@ -471,7 +541,8 @@ ahc_check_cmdcmpltqueues(struct ahc_softc *ahc)
if (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL)
retval |= AHC_RUN_QOUTFIFO;
#ifdef AHC_TARGET_MODE
- if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ 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),
@@ -492,6 +563,15 @@ ahc_intr(struct ahc_softc *ahc)
{
u_int intstat;
+ if ((ahc->pause & INTEN) == 0) {
+ /*
+ * Our interrupt is not enabled on the chip
+ * and may be disabled for re-entrancy reasons,
+ * so just return. This is likely just a shared
+ * interrupt.
+ */
+ return;
+ }
/*
* Instead of directly reading the interrupt status register,
* infer the cause of the interrupt by checking our in-core
@@ -517,10 +597,7 @@ ahc_intr(struct ahc_softc *ahc)
* and asserted the interrupt again.
*/
ahc_flush_device_writes(ahc);
-#ifdef AHC_TARGET_MODE
- if ((ahc->flags & AHC_INITIATORROLE) != 0)
-#endif
- ahc_run_qoutfifo(ahc);
+ ahc_run_qoutfifo(ahc);
#ifdef AHC_TARGET_MODE
if ((ahc->flags & AHC_TARGETROLE) != 0)
ahc_run_tqinfifo(ahc, /*paused*/FALSE);
diff --git a/drivers/scsi/aic7xxx/aic7xxx_linux.c b/drivers/scsi/aic7xxx/aic7xxx_linux.c
deleted file mode 100644
index a8c59f879eb7..000000000000
--- a/drivers/scsi/aic7xxx/aic7xxx_linux.c
+++ /dev/null
@@ -1,2827 +0,0 @@
-/*
- * Adaptec AIC7xxx device driver for Linux.
- *
- * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_linux.c#79 $
- *
- * 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
- *
- */
-
-#include "aic7xxx_osm.h"
-#include "aic7xxx_inline.h"
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
-#include <linux/init.h> /* __setup */
-#endif
-
-#include <linux/mm.h> /* For fetching system memory size */
-#include <linux/blk.h>
-#include <scsi/scsicam.h>
-
-/*
- * 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 5000
-#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)
-{
- printk("(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 :)
- */
-int aic7xxx_no_probe;
-
-/*
- * 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.\n" \
-" verbose Enable verbose/diagnostic logging\n" \
-" no_probe Disable EISA/VLB controller probing\n" \
-" no_reset Supress initial bus resets\n" \
-" extended Enable extended geometry on all controllers\n" \
-" periodic_otag Send an ordered tagged transaction periodically\n" \
-" to prevent tag starvation. This may be\n" \
-" required by some older disk drives/RAID arrays. \n" \
-" reverse_scan Sort PCI devices highest Bus/Slot to lowest\n" \
-" tag_info:<tag_str> Set per-target tag depth\n" \
-" seltime:<int> Selection Timeout(0/256ms,1/128ms,2/64ms,3/32ms)\n" \
-"\n" \
-" Sample /etc/modules.conf line:\n" \
-" Enable verbose logging\n" \
-" Disable EISA/VLB probing\n" \
-" Set tag depth on Controller 2/Target 2 to 10 tags\n" \
-" Shorten the selection timeout to 128ms from its default of 256\n" \
-" \n" \
-" options aic7xxx='\"verbose.no_probe.tag_info:{{}.{}.{..10}}.seltime:1\"'\n"
-);
-#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 int ahc_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag);
-static void ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc);
-static int ahc_linux_slave_configure(Scsi_Device *device);
-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, char *s);
-static int ahc_linux_next_unit(void);
-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 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 void
-ahc_linux_run_device_queues(struct ahc_softc *ahc)
-{
- struct ahc_linux_device *dev;
-
- while ((ahc->flags & AHC_RESOURCE_SHORTAGE) == 0
- && ahc->platform_data->qfrozen == 0
- && (dev = TAILQ_FIRST(&ahc->platform_data->device_runq)) != 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) {
- u_int32_t high_addr;
-
- high_addr = ahc_le32toh(scb->sg_list[0].len)
- & AHC_SG_HIGH_ADDR_MASK;
- pci_unmap_single(ahc->dev_softc,
- ahc_le32toh(scb->sg_list[0].addr)
- | (((dma_addr_t)high_addr) << 8),
- 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);
-}
-
-/************************ Shutdown/halt/reboot hook ***************************/
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-
-static struct notifier_block ahc_linux_notifier = {
- ahc_linux_halt, NULL, 0
-};
-
-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)
-#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)
-#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 *s)
-{
- 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 = strsep(&s, ",.");
-}
-
-/*
- * 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');
-
- while ((p = strsep(&s, ",.")) != NULL) {
- if (!*p) continue;
- 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, s);
- } 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;
- }
- }
- register_reboot_notifier(&ahc_linux_notifier);
- 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;
-
- /*
- * 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')
- printk(KERN_WARNING
-"aic7xxx: Please read the file /usr/src/linux/Documentation/scsi/aic7xxx.txt\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
- template->sg_tablesize = AHC_NSEG;
-
-#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.
- */
- 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);
-
- *((struct ahc_softc **)host->hostdata) = ahc;
- ahc->platform_data->host = host;
- ahc_lock(ahc, &s);
- host->can_queue = AHC_MAX_QUEUE;
- host->cmd_per_lun = 2;
- host->sg_tablesize = AHC_NSEG;
- /* 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);
-}
-
-/*
- * 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) {
- 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);
- }
-}
-
-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->hw_dma_mask = 0xFFFFFFFF;
- /*
- * ahc_lockinit done by scsi_register, as we don't own that lock
- */
- ahc_done_lockinit(ahc);
-#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
- ahc->seltime = (aic7xxx_seltime & 0x3) << 4;
- ahc->seltime_b = (aic7xxx_seltime & 0x3) << 4;
- return (0);
-}
-
-void
-ahc_platform_free(struct ahc_softc *ahc)
-{
- if (ahc->platform_data != NULL) {
- if (ahc->platform_data->host != NULL)
- scsi_unregister(ahc->platform_data->host);
- if (ahc->platform_data->irq)
- 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);
- }
-}
-
-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) {
-
- if (!was_queuing) {
- /*
- * Start out agressively and allow our
- * dynamic queue depth algorithm to take
- * care of the rest.
- */
- dev->maxtags = AHC_MAX_QUEUE;
- 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 = 16;
- }
-
- 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 int
-ahc_linux_slave_configure(Scsi_Device * device)
-{
- struct ahc_softc *ahc;
- u_long flags;
-
- ahc = *((struct ahc_softc **)device->host->hostdata);
- ahc_lock(ahc, &flags);
- ahc_linux_device_queue_depth(ahc, device);
- ahc_unlock(ahc, &flags);
- return 0;
-}
-
-/*
- * 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;
- struct ahc_initiator_tinfo *targ_info;
- struct ahc_tmode_tstate *tstate;
- uint8_t 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);
- targ_info = ahc_fetch_transinfo(ahc, devinfo.channel,
- devinfo.our_scsiid,
- devinfo.target, &tstate);
-
- tags = 0;
- if (device->tagged_supported != 0
- && (ahc->user_discenable & devinfo.target_mask) != 0) {
- if (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;
- } 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;
- }
- }
- if (tags != 0) {
- scsi_adjust_queue_depth(device, MSG_ORDERED_TAG, tags);
- /* 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, device->channel + 'A',
- device->id, device->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;
- */
- scsi_adjust_queue_depth(device, 0, device->host->cmd_per_lun);
- }
-}
-
-/*
- * Queue an SCB to the controller.
- */
-int
-ahc_linux_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *))
-{
- struct ahc_softc *ahc = *(struct ahc_softc **)cmd->host->hostdata;
- struct ahc_linux_device *dev;
-
- /*
- * Save the callback on completion function.
- */
- cmd->scsi_done = scsi_done;
-
- dev = ahc_linux_get_device(ahc, cmd->channel, cmd->target,
- cmd->lun, /*alloc*/TRUE);
- if (dev == NULL) {
- 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);
- }
- 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);
- 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.
- */
- scb->sg_count = 0;
- 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->sg_count = 0;
- 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;
-
- ahc = (struct ahc_softc *) dev_id;
- if (!ahc->platform_data->host) {
- printk("aic7xxx: interrupt while setup incomplete\n");
- return;
- }
- ahc_lock(ahc, &flags);
- ahc_intr(ahc);
- /*
- * It would be nice to run the device queues from a
- * bottom half handler, but as there is no way to
- * dynamically register one, we'll have to postpone
- * that until we get integrated into the kernel.
- */
- ahc_linux_run_device_queues(ahc);
- acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
- TAILQ_INIT(&ahc->platform_data->completeq);
- ahc_unlock(ahc, &flags);
- 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;
- 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));
- 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;
-
- 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
- && 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");
- up(&ahc->platform_data->eh_sem);
- }
-
- 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:
- /*
- * XXX Set a timer and handle ourselves????
- * For now we pray that the mid-layer does something
- * sane for devices that are busy.
- */
- ahc_set_scsi_status(scb, SCSI_STATUS_BUSY);
- 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 *targ_info;
- 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);
- targ_info = ahc_fetch_transinfo(ahc, devinfo.channel,
- devinfo.our_scsiid,
- devinfo.target, &tstate);
- width = targ_info->user.width;
- period = targ_info->user.period;
- offset = targ_info->user.offset;
- ppr_options = targ_info->user.ppr_options;
- minlen = offsetof(struct scsi_inquiry_data, version) + 1;
- if (transferred_len >= minlen) {
- targ_info->curr.protocol_version = SID_ANSI_REV(sid);
-
- /*
- * Only attempt SPI3 once we've verified that
- * the device claims to support SPI3 features.
- */
- if (targ_info->curr.protocol_version < SCSI_REV_2)
- targ_info->curr.transport_version =
- SID_ANSI_REV(sid);
- else
- targ_info->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 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 these bits, we check them to see if any ppr options
- * are available.
- */
- if ((sid->additional_length + 4) >= minlen) {
- if (transferred_len >= minlen
- && (sid->spi3data & SID_SPI_CLOCK_DT) == 0)
- ppr_options = 0;
-
- if (targ_info->curr.protocol_version > SCSI_REV_2)
- targ_info->curr.transport_version = 3;
- else
- ppr_options = 0;
- } 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;
- }
-}
-
-static void
-ahc_linux_sem_timeout(u_long arg)
-{
- struct semaphore *sem;
-
- sem = (struct semaphore *)arg;
- up(sem);
-}
-
-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_linux_run_device_queues(ahc);
- }
- 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);
-}
-
-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");
-
- /*
- * we used to drop io_request_lock and lock ahc from here, but
- * now that the global lock is gone the upper layer have already
- * done what ahc_lock would do /jens
- */
-
- /*
- * 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;
- }
- }
-
- /*
- * 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, 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. Also make sure that we
- * didn't "just" miss an interrupt that would
- * affect this cmd.
- */
- ahc->flags |= AHC_ALL_INTERRUPTS;
- do {
- ahc_intr(ahc);
- ahc_pause(ahc);
- ahc_clear_critical_section(ahc);
- } while (ahc_inb(ahc, INTSTAT) & INT_PEND);
- ahc->flags &= ~AHC_ALL_INTERRUPTS;
- paused = TRUE;
-
- 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;
- }
-
- /*
- * 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) {
- 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);
- }
- ahc_linux_run_device_queues(ahc);
- 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_lock(ahc, &s);
- 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;
-
- ahc = *(struct ahc_softc **)cmd->host->hostdata;
- 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);
-
- ahc_lock(ahc, &s);
- return SUCCESS;
-}
-
-/*
- * Return the disk geometry for the given SCSI device.
- */
-int
-ahc_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
- sector_t capacity, int geom[])
-{
- int heads;
- int sectors;
- int cylinders;
- int ret;
- int extended;
- struct ahc_softc *ahc;
- unsigned char *buf;
-
- ahc = *((struct ahc_softc **)sdev->host->hostdata);
- buf = scsi_bios_ptable(bdev);
-
- if (buf) {
- ret = scsi_partsize(buf, capacity,
- &geom[2], &geom[0], &geom[1]);
- kfree(buf);
- if (ret != -1)
- return (ret);
- }
- heads = 64;
- sectors = 32;
- cylinders = (unsigned long)capacity / (heads * sectors);
-
- if (aic7xxx_extended != 0)
- extended = 1;
- else if (sdev->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 = (unsigned long)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;
-
- if (host != NULL) {
-
- ahc = *(struct ahc_softc **)host->hostdata;
- ahc_free(ahc);
- }
- if (TAILQ_EMPTY(&ahc_tailq)) {
- unregister_reboot_notifier(&ahc_linux_notifier);
-#ifdef CONFIG_PCI
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
- pci_unregister_driver(&aic7xxx_pci_driver);
-#endif
-#endif
- }
- 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++ > 256)
- 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"
-#endif
diff --git a/drivers/scsi/aic7xxx/aic7xxx_linux_host.h b/drivers/scsi/aic7xxx/aic7xxx_linux_host.h
deleted file mode 100644
index d0f5e58170c1..000000000000
--- a/drivers/scsi/aic7xxx/aic7xxx_linux_host.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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_linux_host.h#5 $
- */
-
-#ifndef _AIC7XXX_LINUX_HOST_H_
-#define _AIC7XXX_LINUX_HOST_H_
-
-int ahc_linux_proc_info(char *, char **, off_t, int, int, int);
-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(struct scsi_device *,
- struct block_device *, sector_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.
- */
-#define AIC7XXX { \
- proc_info: ahc_linux_proc_info, \
- detect: ahc_linux_detect, \
- release: ahc_linux_release, \
- info: ahc_linux_info, \
- queuecommand: ahc_linux_queue, \
- eh_abort_handler: ahc_linux_abort, \
- eh_device_reset_handler: ahc_linux_dev_reset, \
- eh_bus_reset_handler: ahc_linux_bus_reset, \
- slave_configure: ahc_linux_slave_configure, \
- bios_param: AIC7XXX_BIOSPARAM, \
- can_queue: 253, /* max simultaneous cmds */\
- this_id: -1, /* scsi id of host adapter */\
- sg_tablesize: 0, /* 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, \
- highmem_io: 1 \
-}
-
-#endif /* _AIC7XXX_LINUX_HOST_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
new file mode 100644
index 000000000000..4a30106757b6
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -0,0 +1,5136 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.c#163 $
+ *
+ * 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 "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include <scsi/scsicam.h>
+
+/*
+ * Include aiclib.c as part of our
+ * "module dependencies are hard" work around.
+ */
+#include "aiclib.c"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+#include <linux/init.h> /* __setup */
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#include "sd.h" /* For geometry detection */
+#endif
+
+#include <linux/mm.h> /* For fetching system memory size */
+#include <linux/blk.h> /* For block_size() */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/unistd.h>
+static int errno;
+
+#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 5000
+#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}
+};
+
+/*
+ * DV option:
+ *
+ * positive value = DV Enabled
+ * zero = DV Disabled
+ * negative value = DV Default for adapter type/seeprom
+ */
+#ifdef CONFIG_AIC7XXX_DV_SETTING
+#define AIC7XXX_CONFIGED_DV CONFIG_AIC7XXX_DV_SETTING
+#else
+#define AIC7XXX_CONFIGED_DV -1
+#endif
+
+static uint8_t aic7xxx_dv_settings[] =
+{
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV
+};
+
+/*
+ * 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)
+{
+ printk("(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
+
+/*
+ * There are lots of broken chipsets in the world. Some of them will
+ * violate the PCI spec when we issue byte sized memory writes to our
+ * controller. I/O mapped register access, if allowed by the given
+ * platform, will work in almost all cases.
+ */
+int aic7xxx_allow_memio = 1;
+
+/*
+ * 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");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+MODULE_PARM(aic7xxx, "s");
+MODULE_PARM_DESC(aic7xxx,
+"period delimited, options string.\n"
+" verbose Enable verbose/diagnostic logging\n"
+" allow_memio Allow device registers to be memory mapped\n"
+" debug Bitmask of debug values to enable\n"
+" no_probe Disable EISA/VLB controller probing\n"
+" no_reset Supress initial bus resets\n"
+" extended Enable extended geometry on all controllers\n"
+" periodic_otag Send an ordered tagged transaction\n"
+" periodically to prevent tag starvation.\n"
+" This may be required by some older disk\n"
+" drives or RAID arrays.\n"
+" reverse_scan Sort PCI devices highest Bus/Slot to lowest\n"
+" tag_info:<tag_str> Set per-target tag depth\n"
+" global_tag_depth:<int> Global tag depth for every target\n"
+" on every bus\n"
+" dv:<dv_settings> Set per-controller Domain Validation Setting.\n"
+" seltime:<int> Selection Timeout\n"
+" (0/256ms,1/128ms,2/64ms,3/32ms)\n"
+"\n"
+" Sample /etc/modules.conf line:\n"
+" Enable verbose logging\n"
+" Disable EISA/VLB probing\n"
+" Set tag depth on Controller 1/Target 2 to 10 tags\n"
+" Shorten the selection timeout to 128ms\n"
+"\n"
+" options aic7xxx='\"verbose.no_probe.tag_info:{{}.{..10}}.seltime:1\"'\n"
+);
+#endif
+
+static void ahc_linux_handle_scsi_status(struct ahc_softc *,
+ struct ahc_linux_device *,
+ struct scb *);
+static void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc,
+ Scsi_Cmnd *cmd);
+static void ahc_linux_filter_inquiry(struct ahc_softc*, struct ahc_devinfo*);
+static void ahc_linux_sem_timeout(u_long arg);
+static void ahc_linux_freeze_simq(struct ahc_softc *ahc);
+static void ahc_linux_release_simq(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_thread_run_complete_queue(struct ahc_softc *ahc);
+static void ahc_linux_start_dv(struct ahc_softc *ahc);
+static void ahc_linux_dv_timeout(struct scsi_cmnd *cmd);
+static int ahc_linux_dv_thread(void *data);
+static void ahc_linux_dv_target(struct ahc_softc *ahc, u_int target);
+static void ahc_linux_dv_transition(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static uint32_t aic_dv_error_action(struct scsi_cmnd *cmd,
+ struct scsi_inquiry_data *inq_data);
+static void ahc_linux_dv_fill_cmd(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo);
+static void ahc_linux_dv_inq(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ,
+ u_int request_length);
+static void ahc_linux_dv_tur(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo);
+static void ahc_linux_dv_rebd(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static void ahc_linux_dv_web(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static void ahc_linux_dv_reb(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static void ahc_linux_dv_su(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static int ahc_linux_fallback(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_linux_dv_complete(Scsi_Cmnd *cmd);
+static void ahc_linux_generate_dv_pattern(struct ahc_linux_target *targ);
+static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static u_int ahc_linux_user_dv_setting(struct ahc_softc *ahc);
+static void ahc_linux_device_queue_depth(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev);
+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, char *s);
+static void ahc_linux_setup_tag_info_global(char *p);
+static void ahc_linux_setup_dv(char *p, char *end, char *s);
+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 void ahc_schedule_completeq(struct ahc_softc *ahc, struct ahc_cmd *acmd);
+
+/********************************* Inlines ************************************/
+static __inline void ahc_schedule_runq(struct ahc_softc *ahc);
+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 struct ahc_cmd *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_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 void
+ahc_schedule_completeq(struct ahc_softc *ahc, struct ahc_cmd *acmd)
+{
+ while (acmd != NULL) {
+ struct ahc_completeq *completeq;
+ struct ahc_cmd *list_cmd;
+ struct ahc_cmd *next_cmd;
+
+ next_cmd = TAILQ_NEXT(acmd, acmd_links.tqe);
+ completeq = &ahc->platform_data->completeq;
+ list_cmd = TAILQ_FIRST(completeq);
+ 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);
+ acmd = next_cmd;
+ }
+ if ((ahc->platform_data->flags & AHC_RUN_CMPLT_Q_TIMER) == 0) {
+ ahc->platform_data->flags |= AHC_RUN_CMPLT_Q_TIMER;
+ ahc->platform_data->completeq_timer.expires = jiffies;
+ add_timer(&ahc->platform_data->completeq_timer);
+ }
+}
+
+static __inline void
+ahc_schedule_runq(struct ahc_softc *ahc)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ tasklet_schedule(&ahc->platform_data->runq_tasklet);
+#else
+ /*
+ * Tasklets are not available, so run inline.
+ */
+ ahc_runq_tasklet((unsigned long)ahc);
+#endif
+}
+
+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);
+}
+
+#define AHC_LINUX_MAX_RETURNED_ERRORS 4
+static struct ahc_cmd *
+ahc_linux_run_complete_queue(struct ahc_softc *ahc, struct ahc_cmd *acmd)
+{
+ u_long done_flags;
+ int with_errors;
+
+ ahc_done_lock(ahc, &done_flags);
+ with_errors = 0;
+ while (acmd != NULL) {
+ Scsi_Cmnd *cmd;
+
+ cmd = &acmd_scsi_cmd(acmd);
+ acmd = TAILQ_NEXT(acmd, acmd_links.tqe);
+ cmd->host_scribble = NULL;
+ if (ahc_cmd_get_transaction_status(cmd) != DID_OK
+ || (cmd->result & 0xFF) != SCSI_STATUS_OK)
+ with_errors++;
+
+ cmd->scsi_done(cmd);
+
+ if (with_errors > AHC_LINUX_MAX_RETURNED_ERRORS) {
+ /*
+ * Linux uses stack recursion to requeue
+ * commands that need to be retried. Avoid
+ * blowing out the stack by "spoon feeding"
+ * commands that completed with error back
+ * the operating system in case they are going
+ * to be retried. "ick"
+ */
+ break;
+ }
+ }
+ ahc_done_unlock(ahc, &done_flags);
+ return (acmd);
+}
+
+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
+ && AHC_DV_SIMQ_FROZEN(ahc) == 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_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);
+}
+
+/************************ Host template entry points *************************/
+static int ahc_linux_detect(Scsi_Host_Template *);
+static int ahc_linux_release(struct Scsi_Host *);
+static int ahc_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+static const char *ahc_linux_info(struct Scsi_Host *);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int ahc_linux_slave_alloc(Scsi_Device *);
+static int ahc_linux_slave_configure(Scsi_Device *);
+static void ahc_linux_slave_destroy(Scsi_Device *);
+static int ahc_linux_biosparam(struct scsi_device*,
+ struct block_device*,
+ sector_t, int[]);
+#else
+static void ahc_linux_select_queue_depth(struct Scsi_Host *host,
+ Scsi_Device *scsi_devs);
+static int ahc_linux_biosparam(Disk *, kdev_t, int[]);
+#endif
+static int ahc_linux_bus_reset(Scsi_Cmnd *);
+static int ahc_linux_dev_reset(Scsi_Cmnd *);
+static int ahc_linux_abort(Scsi_Cmnd *);
+
+/*
+ * Try to detect an Adaptec 7XXX controller.
+ */
+static int
+ahc_linux_detect(Scsi_Host_Template *template)
+{
+ struct ahc_softc *ahc;
+ int found;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+#endif
+
+ /*
+ * 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')
+ printk(KERN_WARNING
+"aic7xxx: Please read the file /usr/src/linux/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
+
+ /*
+ * 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.
+ */
+ found = 0;
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+
+ if (ahc_linux_register_host(ahc, template) == 0)
+ found++;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_lock_irq(&io_request_lock);
+#endif
+ aic7xxx_detect_complete++;
+ return (found);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * Return a string describing the driver.
+ */
+static 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);
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+static 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_midlayer_entrypoint_lock(ahc, &flags);
+
+ /*
+ * Close the race of a command that was in the process of
+ * being queued to us just as our simq was frozen. Let
+ * DV commands through so long as we are only frozen to
+ * perform DV.
+ */
+ if (ahc->platform_data->qfrozen != 0
+ && AHC_DV_CMD(cmd) == 0) {
+
+ ahc_cmd_set_transaction_status(cmd, CAM_REQUEUE_REQ);
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+ ahc_schedule_completeq(ahc, NULL);
+ ahc_midlayer_entrypoint_unlock(ahc, &flags);
+ return (0);
+ }
+ dev = ahc_linux_get_device(ahc, cmd->channel, cmd->target,
+ cmd->lun, /*alloc*/TRUE);
+ if (dev == NULL) {
+ ahc_midlayer_entrypoint_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_midlayer_entrypoint_unlock(ahc, &flags);
+ return (0);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int
+ahc_linux_slave_alloc(Scsi_Device *device)
+{
+ struct ahc_softc *ahc;
+
+ ahc = *((struct ahc_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Alloc %d\n", ahc_name(ahc), device->id);
+ return (0);
+}
+
+static int
+ahc_linux_slave_configure(Scsi_Device *device)
+{
+ struct ahc_softc *ahc;
+ struct ahc_linux_device *dev;
+ u_long flags;
+
+ ahc = *((struct ahc_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Configure %d\n", ahc_name(ahc), device->id);
+ ahc_midlayer_entrypoint_lock(ahc, &flags);
+ /*
+ * Since Linux has attached to the device, configure
+ * it so we don't free and allocate the device
+ * structure on every command.
+ */
+ dev = ahc_linux_get_device(ahc, device->channel,
+ device->id, device->lun,
+ /*alloc*/TRUE);
+ if (dev != NULL) {
+ dev->flags &= ~AHC_DEV_UNCONFIGURED;
+ dev->scsi_device = device;
+ ahc_linux_device_queue_depth(ahc, dev);
+ }
+ ahc_midlayer_entrypoint_unlock(ahc, &flags);
+ return (0);
+}
+
+static void
+ahc_linux_slave_destroy(Scsi_Device *device)
+{
+ struct ahc_softc *ahc;
+ struct ahc_linux_device *dev;
+ u_long flags;
+
+ ahc = *((struct ahc_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Destroy %d\n", ahc_name(ahc), device->id);
+ ahc_midlayer_entrypoint_lock(ahc, &flags);
+ dev = ahc_linux_get_device(ahc, device->channel,
+ device->id, device->lun,
+ /*alloc*/FALSE);
+ /*
+ * Filter out "silly" deletions of real devices by only
+ * deleting devices that have had slave_configure()
+ * called on them. All other devices that have not
+ * been configured will automatically be deleted by
+ * the refcounting process.
+ */
+ if (dev != NULL
+ && (dev->flags & AHC_DEV_SLAVE_CONFIGURED) != 0) {
+ dev->flags |= AHC_DEV_UNCONFIGURED;
+ if (TAILQ_EMPTY(&dev->busyq)
+ && dev->active == 0)
+ ahc_linux_free_device(ahc, dev);
+ }
+ ahc_midlayer_entrypoint_unlock(ahc, &flags);
+}
+#else
+/*
+ * 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;
+
+ ahc = *((struct ahc_softc **)host->hostdata);
+ ahc_midlayer_entrypoint_lock(ahc, &flags);
+ for (device = scsi_devs; device != NULL; device = device->next) {
+ if (device->host == host) {
+ struct ahc_linux_device *dev;
+
+ /*
+ * Since Linux has attached to the device, configure
+ * it so we don't free and allocate the device
+ * structure on every command.
+ */
+ dev = ahc_linux_get_device(ahc, device->channel,
+ device->id, device->lun,
+ /*alloc*/TRUE);
+ if (dev != NULL) {
+ dev->flags &= ~AHC_DEV_UNCONFIGURED;
+ dev->scsi_device = device;
+ ahc_linux_device_queue_depth(ahc, dev);
+ device->queue_depth = dev->openings
+ + dev->active;
+ if ((dev->flags & (AHC_DEV_Q_BASIC
+ | AHC_DEV_Q_TAGGED)) == 0) {
+ /*
+ * 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;
+ }
+ }
+ }
+ }
+ ahc_midlayer_entrypoint_unlock(ahc, &flags);
+}
+#endif
+
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ahc_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ uint8_t *bh;
+#else
+ahc_linux_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+ struct scsi_device *sdev = disk->device;
+ u_long capacity = disk->capacity;
+ struct buffer_head *bh;
+#endif
+ int heads;
+ int sectors;
+ int cylinders;
+ int ret;
+ int extended;
+ struct ahc_softc *ahc;
+ u_int channel;
+
+ ahc = *((struct ahc_softc **)sdev->host->hostdata);
+ channel = sdev->channel;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ bh = scsi_bios_ptable(bdev);
+#elif 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, capacity,
+ &geom[2], &geom[0], &geom[1]);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ kfree(bh);
+#else
+ brelse(bh);
+#endif
+ if (ret != -1)
+ return (ret);
+ }
+ heads = 64;
+ sectors = 32;
+ cylinders = aic_sector_div(capacity, heads, sectors);
+
+ if (aic7xxx_extended != 0)
+ extended = 1;
+ else if (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 = aic_sector_div(capacity, heads, sectors);
+ }
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ return (0);
+}
+
+/*
+ * Abort the current SCSI command(s).
+ */
+static 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.
+ */
+static 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.
+ */
+static int
+ahc_linux_bus_reset(Scsi_Cmnd *cmd)
+{
+ struct ahc_softc *ahc;
+ struct ahc_cmd *acmd;
+ u_long s;
+ int found;
+
+ ahc = *(struct ahc_softc **)cmd->host->hostdata;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_unlock_irq(&io_request_lock);
+#endif
+ ahc_midlayer_entrypoint_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_midlayer_entrypoint_unlock(ahc, &s);
+ if (bootverbose)
+ printf("%s: SCSI bus reset delivered. "
+ "%d SCBs aborted.\n", ahc_name(ahc), found);
+
+ if (acmd != NULL) {
+ acmd = ahc_linux_run_complete_queue(ahc, acmd);
+ if (acmd != NULL) {
+ ahc_midlayer_entrypoint_lock(ahc, &s);
+ ahc_schedule_completeq(ahc, acmd);
+ ahc_midlayer_entrypoint_unlock(ahc, &s);
+ }
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_lock_irq(&io_request_lock);
+#endif
+ return SUCCESS;
+}
+
+Scsi_Host_Template aic7xxx_driver_template = {
+ .proc_info = ahc_linux_proc_info,
+ .detect = ahc_linux_detect,
+ .release = ahc_linux_release,
+ .info = ahc_linux_info,
+ .queuecommand = ahc_linux_queue,
+ .eh_abort_handler = ahc_linux_abort,
+ .eh_device_reset_handler = ahc_linux_dev_reset,
+ .eh_bus_reset_handler = ahc_linux_bus_reset,
+#if defined(__i386__)
+ .bios_param = ahc_linux_biosparam,
+#endif
+ .can_queue = AHC_MAX_QUEUE,
+ .this_id = -1,
+ .sg_tablesize = AHC_NSEG,
+ .cmd_per_lun = 2,
+ .use_clustering = ENABLE_CLUSTERING,
+#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.
+ */
+ .max_sectors = 8192,
+#endif
+#if defined CONFIG_HIGHIO
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+/* Assume RedHat Distribution with its different HIGHIO conventions. */
+ .can_dma_32 = 1,
+ .single_sg_okay = 1,
+#else
+ .highmem_io = 1,
+#endif
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ .name = "aic7xxx",
+ .slave_alloc = ahc_linux_slave_alloc,
+ .slave_configure = ahc_linux_slave_configure,
+ .slave_destroy = ahc_linux_slave_destroy,
+#else
+ .select_queue_depths = ahc_linux_select_queue_depth,
+ .use_new_eh_code = 1,
+#endif
+};
+
+#define driver_template aic7xxx_driver_template
+#include "scsi_module.c"
+
+/**************************** 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 <linux/notifier.h>
+#include <linux/reboot.h>
+
+static struct notifier_block ahc_linux_notifier = {
+ ahc_linux_halt, NULL, 0
+};
+
+static int ahc_linux_halt(struct notifier_block *nb, u_long event, void *buf)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ struct ahc_softc *ahc;
+
+ /*
+ * In 2.5.X, this is called prior to the filesystems
+ * being synced and the SCSI layer being properly
+ * shutdown. A different API is required there,
+ * but the device hooks for this don't quite look
+ * right.
+ */
+ if (event == SYS_DOWN || event == SYS_HALT) {
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+ ahc_shutdown(ahc);
+ }
+ }
+#endif
+ 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)
+ ahc_pci_set_dma_mask(ahc->dev_softc, 0xFFFFFFFF);
+ *vaddr = pci_alloc_consistent(ahc->dev_softc,
+ dmat->maxsize, &map->bus_addr);
+ if (ahc->dev_softc != NULL)
+ ahc_pci_set_dma_mask(ahc->dev_softc,
+ ahc->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
+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 *s)
+{
+ 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 = strsep(&s, ",.");
+}
+
+static void
+ahc_linux_setup_tag_info_global(char *p)
+{
+ int tags, i, j;
+
+ tags = simple_strtoul(p + 1, NULL, 0) & 0xff;
+ printf("Setting Global Tags= %d\n", tags);
+
+ for (i = 0; i < NUM_ELEMENTS(aic7xxx_tag_info); i++) {
+ for (j = 0; j < AHC_NUM_TARGETS; j++) {
+ aic7xxx_tag_info[i].tag_commands[j] = tags;
+ }
+ }
+}
+
+static void
+ahc_linux_setup_dv(char *p, char *end, char *s)
+{
+ char *base;
+ char *tok;
+ char *tok_end;
+ char *tok_end2;
+ int i;
+ int instance;
+ int done;
+ char tok_list[] = {'.', ',', '{', '}', '\0'};
+
+ if (*p != ':')
+ return;
+
+ instance = -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(aic7xxx_dv_settings))
+ 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(aic7xxx_dv_settings))) {
+ aic7xxx_dv_settings[instance] =
+ simple_strtol(tok, NULL, 0);
+ }
+ tok = tok_end;
+ break;
+ }
+ }
+ while ((p != base) && (p != NULL))
+ p = strsep(&s, ",.");
+}
+
+/*
+ * 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 },
+ { "allow_memio", &aic7xxx_allow_memio},
+#ifdef AHC_DEBUG
+ { "debug", &ahc_debug },
+#endif
+ { "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 },
+ { "global_tag_depth", NULL },
+ { "dv", NULL }
+ };
+
+ end = strchr(s, '\0');
+
+ while ((p = strsep(&s, ",.")) != NULL) {
+ if (*p == '\0')
+ continue;
+ 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, "global_tag_depth", n)) {
+ ahc_linux_setup_tag_info_global(p + n);
+ } else if (!strncmp(p, "tag_info", n)) {
+ ahc_linux_setup_tag_info(p + n, end, s);
+ } else if (strncmp(p, "dv", n) == 0) {
+ ahc_linux_setup_dv(p + n, end, s);
+ } 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;
+
+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;
+ u_int target;
+
+ template->name = ahc->description;
+ host = scsi_register(template, sizeof(struct ahc_softc *));
+ if (host == NULL)
+ return (ENOMEM);
+
+ *((struct ahc_softc **)host->hostdata) = ahc;
+ ahc_lock(ahc, &s);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ scsi_assign_lock(host, &ahc->platform_data->spin_lock);
+#endif
+ ahc->platform_data->host = host;
+ host->can_queue = AHC_MAX_QUEUE;
+ host->cmd_per_lun = 2;
+ host->sg_tablesize = AHC_NSEG;
+ /* 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);
+ ahc->platform_data->dv_pid = kernel_thread(ahc_linux_dv_thread, ahc, 0);
+ ahc_lock(ahc, &s);
+ if (ahc->platform_data->dv_pid < 0) {
+ printf("%s: Failed to create DV thread, error= %d\n",
+ ahc_name(ahc), ahc->platform_data->dv_pid);
+ return (-ahc->platform_data->dv_pid);
+ }
+ /*
+ * Initially allocate *all* of our linux target objects
+ * so that the DV thread will scan them all in parallel
+ * just after driver initialization. Any device that
+ * does not exist will have its target object destroyed
+ * by the selection timeout handler. In the case of a
+ * device that appears after the initial DV scan, async
+ * negotiation will occur for the first command, and DV
+ * will comence should that first command be successful.
+ */
+ for (target = 0; target < AHC_NUM_TARGETS; target++) {
+ u_int channel;
+
+ channel = 0;
+ if (target > 7
+ && (ahc->features & AHC_TWIN) != 0)
+ channel = 1;
+ ahc_linux_alloc_target(ahc, channel, target);
+ }
+ ahc_intr_enable(ahc, TRUE);
+ ahc_linux_start_dv(ahc);
+ ahc_unlock(ahc, &s);
+ return (0);
+}
+
+uint64_t
+ahc_linux_get_memsize()
+{
+ struct sysinfo si;
+
+ si_meminfo(&si);
+ return ((uint64_t)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
+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;
+ }
+ }
+
+ /*
+ * Force negotiation to async for all targets that
+ * will not see an initial bus reset.
+ */
+ 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);
+ ahc_compile_devinfo(&devinfo, our_id, target_id,
+ CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);
+ ahc_update_neg_request(ahc, &devinfo, tstate,
+ tinfo, AHC_NEG_ALWAYS);
+ }
+ /* Give the bus some time to recover */
+ if ((ahc->flags & (AHC_RESET_BUS_A|AHC_RESET_BUS_B)) != 0) {
+ ahc_linux_freeze_simq(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_simq;
+ add_timer(&ahc->platform_data->reset_timer);
+ }
+}
+
+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);
+ init_timer(&ahc->platform_data->completeq_timer);
+ ahc->platform_data->completeq_timer.data = (u_long)ahc;
+ ahc->platform_data->completeq_timer.function =
+ (ahc_linux_callback_t *)ahc_linux_thread_run_complete_queue;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ init_MUTEX_LOCKED(&ahc->platform_data->eh_sem);
+ init_MUTEX_LOCKED(&ahc->platform_data->dv_sem);
+ init_MUTEX_LOCKED(&ahc->platform_data->dv_cmd_sem);
+#else
+ ahc->platform_data->eh_sem = MUTEX_LOCKED;
+ ahc->platform_data->dv_sem = MUTEX_LOCKED;
+ ahc->platform_data->dv_cmd_sem = MUTEX_LOCKED;
+#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 (TAILQ_EMPTY(&ahc_tailq))
+ register_reboot_notifier(&ahc_linux_notifier);
+ return (0);
+}
+
+void
+ahc_platform_free(struct ahc_softc *ahc)
+{
+ u_long s;
+
+ if (ahc->platform_data != NULL) {
+ /* Kill the DV kthread */
+ if (ahc->platform_data->dv_pid > 0) {
+ ahc_lock(ahc, &s);
+ ahc->platform_data->flags |= AHC_DV_SHUTDOWN;
+ ahc_unlock(ahc, &s);
+ up(&ahc->platform_data->dv_sem);
+ do {
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ printf("%s: Waiting for DV thread to "
+ "exit\n", ahc_name(ahc));
+ }
+#endif
+ } while (waitpid(ahc->platform_data->dv_pid, NULL,
+ __WCLONE) == -ERESTARTSYS);
+ }
+#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)) {
+ unregister_reboot_notifier(&ahc_linux_notifier);
+#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 (dev->maxtags == 0) {
+ /*
+ * Queueing is disabled by the user.
+ */
+ dev->openings = 1;
+ } else 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;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ if (dev->scsi_device != NULL) {
+ switch ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED))) {
+ case AHC_DEV_Q_BASIC:
+ scsi_adjust_queue_depth(dev->scsi_device,
+ MSG_SIMPLE_TASK,
+ dev->openings + dev->active);
+ break;
+ case AHC_DEV_Q_TAGGED:
+ scsi_adjust_queue_depth(dev->scsi_device,
+ MSG_ORDERED_TASK,
+ dev->openings + dev->active);
+ break;
+ default:
+ /*
+ * 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.
+ */
+ scsi_adjust_queue_depth(dev->scsi_device,
+ /*NON-TAGGED*/0,
+ /*queue depth*/2);
+ break;
+ }
+ }
+#endif
+}
+
+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);
+}
+
+static void
+ahc_linux_thread_run_complete_queue(struct ahc_softc *ahc)
+{
+ struct ahc_cmd *acmd;
+ u_long flags;
+
+ ahc_lock(ahc, &flags);
+ del_timer(&ahc->platform_data->completeq_timer);
+ ahc->platform_data->flags &= ~AHC_RUN_CMPLT_Q_TIMER;
+ acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+ TAILQ_INIT(&ahc->platform_data->completeq);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &flags);
+#endif
+ if (acmd != NULL) {
+ acmd = ahc_linux_run_complete_queue(ahc, acmd);
+ if (acmd != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_lock(ahc, &flags);
+#endif
+ ahc_schedule_completeq(ahc, acmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &flags);
+#endif
+ }
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &flags);
+#endif
+}
+
+static void
+ahc_linux_start_dv(struct ahc_softc *ahc)
+{
+
+ /*
+ * Freeze the simq and signal ahc_linux_queue to not let any
+ * more commands through
+ */
+ if ((ahc->platform_data->flags & AHC_DV_ACTIVE) == 0) {
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("%s: Waking DV thread\n", ahc_name(ahc));
+#endif
+
+ ahc->platform_data->flags |= AHC_DV_ACTIVE;
+ ahc_linux_freeze_simq(ahc);
+
+ /* Wake up the DV kthread */
+ up(&ahc->platform_data->dv_sem);
+ }
+}
+
+static int
+ahc_linux_dv_thread(void *data)
+{
+ struct ahc_softc *ahc;
+ int target;
+ u_long s;
+
+ ahc = (struct ahc_softc *)data;
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("Launching DV Thread\n");
+#endif
+
+ while (1) {
+ down(&ahc->platform_data->dv_sem);
+
+ /* Check to see if we've been signaled to exit */
+ ahc_lock(ahc, &s);
+ if ((ahc->platform_data->flags & AHC_DV_SHUTDOWN) != 0) {
+ ahc_unlock(ahc, &s);
+ return (0);
+ }
+ ahc_unlock(ahc, &s);
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("%s: Beginning Domain Validation\n",
+ ahc_name(ahc));
+#endif
+
+ /*
+ * Wait for any pending commands to drain before proceeding.
+ */
+ ahc_lock(ahc, &s);
+ while (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+ ahc->platform_data->flags |= AHC_DV_WAIT_SIMQ_EMPTY;
+ ahc_unlock(ahc, &s);
+ down(&ahc->platform_data->dv_sem);
+ ahc_lock(ahc, &s);
+ }
+
+ /*
+ * Wait for the SIMQ to be released so that DV is the
+ * only reason the queue is frozen.
+ */
+ while (AHC_DV_SIMQ_FROZEN(ahc) == 0) {
+ ahc->platform_data->flags |= AHC_DV_WAIT_SIMQ_RELEASE;
+ ahc_unlock(ahc, &s);
+ down(&ahc->platform_data->dv_sem);
+ ahc_lock(ahc, &s);
+ }
+ ahc_unlock(ahc, &s);
+
+ for (target = 0; target < AHC_NUM_TARGETS; target++)
+ ahc_linux_dv_target(ahc, target);
+
+ ahc_lock(ahc, &s);
+ ahc->platform_data->flags &= ~AHC_DV_ACTIVE;
+ ahc_unlock(ahc, &s);
+
+ /*
+ * Release the SIMQ so that normal commands are
+ * allowed to continue on the bus.
+ */
+ ahc_linux_release_simq((u_long)ahc);
+ }
+
+ return (0);
+}
+
+#define AHC_LINUX_DV_INQ_SHORT_LEN 36
+#define AHC_LINUX_DV_INQ_LEN 256
+#define AHC_LINUX_DV_TIMEOUT (HZ / 4)
+
+#define AHC_SET_DV_STATE(ahc, targ, newstate) \
+ ahc_set_dv_state(ahc, targ, newstate, __LINE__)
+
+static __inline void
+ahc_set_dv_state(struct ahc_softc *ahc, struct ahc_linux_target *targ,
+ ahc_dv_state newstate, u_int line)
+{
+ ahc_dv_state oldstate;
+
+ oldstate = targ->dv_state;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("%s:%d: Going from state %d to state %d\n",
+ ahc_name(ahc), line, oldstate, newstate);
+#endif
+
+ if (oldstate == newstate)
+ targ->dv_state_retry++;
+ else
+ targ->dv_state_retry = 0;
+ targ->dv_state = newstate;
+}
+
+static void
+ahc_linux_dv_target(struct ahc_softc *ahc, u_int target_offset)
+{
+ struct ahc_devinfo devinfo;
+ struct ahc_linux_target *targ;
+ struct scsi_cmnd *cmd;
+ struct scsi_sense_data *sense;
+ uint8_t *buffer;
+ u_long s;
+ u_int timeout;
+ int echo_size;
+
+ sense = NULL;
+ buffer = NULL;
+ echo_size = 0;
+ ahc_lock(ahc, &s);
+ targ = ahc->platform_data->targets[target_offset];
+ if (targ == NULL || (targ->flags & AHC_DV_REQUIRED) == 0) {
+ ahc_unlock(ahc, &s);
+ return;
+ }
+ ahc_compile_devinfo(&devinfo, ahc->our_id, targ->target, /*lun*/0,
+ targ->channel + 'A', ROLE_INITIATOR);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Performing DV\n");
+ }
+#endif
+
+ ahc_unlock(ahc, &s);
+
+ cmd = malloc(sizeof(struct scsi_cmnd), M_DEVBUF, M_WAITOK);
+
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_INQ_SHORT_ASYNC);
+
+ while (targ->dv_state != AHC_DV_STATE_EXIT) {
+ timeout = AHC_LINUX_DV_TIMEOUT;
+ switch (targ->dv_state) {
+ case AHC_DV_STATE_INQ_SHORT_ASYNC:
+ case AHC_DV_STATE_INQ_ASYNC:
+ case AHC_DV_STATE_INQ_ASYNC_VERIFY:
+ /*
+ * Set things to async narrow to reduce the
+ * chance that the INQ will fail.
+ */
+ ahc_lock(ahc, &s);
+ ahc_set_syncrate(ahc, &devinfo, NULL, 0, 0, 0,
+ AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_unlock(ahc, &s);
+ timeout = 10 * HZ;
+ /* FALLTHROUGH */
+ case AHC_DV_STATE_INQ_VERIFY:
+ {
+ u_int inq_len;
+
+ if (targ->dv_state == AHC_DV_STATE_INQ_SHORT_ASYNC)
+ inq_len = AHC_LINUX_DV_INQ_SHORT_LEN;
+ else
+ inq_len = targ->inq_data->additional_length + 5;
+ ahc_linux_dv_inq(ahc, cmd, &devinfo, targ, inq_len);
+ break;
+ }
+ case AHC_DV_STATE_TUR:
+ case AHC_DV_STATE_BUSY:
+ ahc_linux_dv_tur(ahc, cmd, &devinfo);
+ break;
+ case AHC_DV_STATE_REBD:
+ ahc_linux_dv_rebd(ahc, cmd, &devinfo, targ);
+ break;
+ case AHC_DV_STATE_WEB:
+ ahc_linux_dv_web(ahc, cmd, &devinfo, targ);
+ break;
+
+ case AHC_DV_STATE_REB:
+ ahc_linux_dv_reb(ahc, cmd, &devinfo, targ);
+ break;
+
+ case AHC_DV_STATE_SU:
+ ahc_linux_dv_su(ahc, cmd, &devinfo, targ);
+ timeout = 50 * HZ;
+ break;
+
+ default:
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Unknown DV state %d\n", targ->dv_state);
+ goto out;
+ }
+
+ /* Queue the command and wait for it to complete */
+ /* Abuse eh_timeout in the scsi_cmnd struct for our purposes */
+ init_timer(&cmd->eh_timeout);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ /*
+ * All of the printfs during negotiation
+ * really slow down the negotiation.
+ * Add a bit of time just to be safe.
+ */
+ timeout += HZ;
+#endif
+ scsi_add_timer(cmd, timeout, ahc_linux_dv_timeout);
+ /*
+ * In 2.5.X, it is assumed that all calls from the
+ * "midlayer" (which we are emulating) will have the
+ * ahc host lock held.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahc_lock(ahc, &s);
+#endif
+ ahc_linux_queue(cmd, ahc_linux_dv_complete);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &s);
+#endif
+ down(&ahc->platform_data->dv_cmd_sem);
+ /*
+ * Wait for the SIMQ to be released so that DV is the
+ * only reason the queue is frozen.
+ */
+ ahc_lock(ahc, &s);
+ while (AHC_DV_SIMQ_FROZEN(ahc) == 0) {
+ ahc->platform_data->flags |= AHC_DV_WAIT_SIMQ_RELEASE;
+ ahc_unlock(ahc, &s);
+ down(&ahc->platform_data->dv_sem);
+ ahc_lock(ahc, &s);
+ }
+ ahc_unlock(ahc, &s);
+
+ ahc_linux_dv_transition(ahc, cmd, &devinfo, targ);
+ }
+
+out:
+ if (cmd != NULL)
+ free(cmd, M_DEVBUF);
+
+ ahc_lock(ahc, &s);
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->flags &= ~AHC_DV_REQUIRED;
+ if (targ->refcount == 0)
+ ahc_linux_free_target(ahc, targ);
+ ahc_unlock(ahc, &s);
+}
+
+static void
+ahc_linux_dv_transition(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ)
+{
+ u_int32_t status;
+
+ status = aic_dv_error_action(cmd, targ->inq_data);
+
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Entering ahc_linux_dv_transition, state= %d, "
+ "status= 0x%x, cmd->result= 0x%x\n", targ->dv_state,
+ status, cmd->result);
+ }
+#endif
+
+ switch (targ->dv_state) {
+ case AHC_DV_STATE_INQ_SHORT_ASYNC:
+ case AHC_DV_STATE_INQ_ASYNC:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state+1);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+ if ((status & SS_ERRMASK) == EBUSY)
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+ case AHC_DV_STATE_INQ_ASYNC_VERIFY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ u_int spi3data;
+
+ if (memcmp(targ->inq_data, targ->dv_buffer,
+ AHC_LINUX_DV_INQ_LEN) != 0) {
+ /*
+ * Inquiry data must have changed.
+ * Try from the top again.
+ */
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ }
+
+ if (ahc_linux_user_dv_setting(ahc) == 0) {
+ ahc_linux_filter_inquiry(ahc, devinfo);
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+
+ spi3data = targ->inq_data->spi3data;
+ switch (spi3data & SID_SPI_CLOCK_DT_ST) {
+ default:
+ case SID_SPI_CLOCK_ST:
+ /* Assume only basic DV is supported. */
+ ahc_linux_filter_inquiry(ahc, devinfo);
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_VERIFY);
+ break;
+ case SID_SPI_CLOCK_DT:
+ case SID_SPI_CLOCK_DT_ST:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_REBD);
+ break;
+ }
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+
+ if ((status & SS_ERRMASK) == EBUSY)
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+ case AHC_DV_STATE_INQ_VERIFY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+
+ if (memcmp(targ->inq_data, targ->dv_buffer,
+ AHC_LINUX_DV_INQ_LEN) == 0) {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ } else if ((status & SS_ERRMASK) == EBUSY)
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_TUR:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_ASYNC);
+ break;
+ case SS_RETRY:
+ case SS_TUR:
+ if ((status & SS_ERRMASK) == EBUSY) {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
+ break;
+ }
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ }
+ if (targ->dv_state_retry >= 10) {
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV TUR reties exhausted\n");
+ }
+#endif
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ if (status & SSQ_DELAY)
+ scsi_sleep(1 * HZ);
+
+ break;
+ case SS_START:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_SU);
+ break;
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_REBD:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ uint32_t echo_size;
+
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_WEB);
+ echo_size = scsi_3btoul(&targ->dv_buffer[1]);
+ echo_size &= 0x1FFF;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Echo buffer size= %d\n", echo_size);
+ }
+#endif
+ if (echo_size == 0) {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+
+ /* Generate the buffer pattern */
+ targ->dv_echo_size = echo_size;
+ ahc_linux_generate_dv_pattern(targ);
+ /*
+ * Setup initial negotiation values.
+ */
+ ahc_linux_filter_inquiry(ahc, devinfo);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+ if (targ->dv_state_retry <= 10)
+ break;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV REBD reties exhausted\n");
+ }
+#endif
+ /* FALLTHROUGH */
+ case SS_FATAL:
+ default:
+ /*
+ * Setup initial negotiation values
+ * and try level 1 DV.
+ */
+ ahc_linux_filter_inquiry(ahc, devinfo);
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_INQ_VERIFY);
+ targ->dv_echo_size = 0;
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_WEB:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_REB);
+ break;
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ }
+ if (targ->dv_state_retry <= 10)
+ break;
+ /* FALLTHROUGH */
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV WEB reties exhausted\n");
+ }
+#endif
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_REB:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ if (memcmp(targ->dv_buffer, targ->dv_buffer1,
+ targ->dv_echo_size) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0)
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ else
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_WEB);
+ break;
+ }
+
+ if (targ->dv_buffer != NULL) {
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = NULL;
+ }
+ if (targ->dv_buffer1 != NULL) {
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = NULL;
+ }
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ break;
+ }
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_WEB);
+ }
+ if (targ->dv_state_retry <= 10) {
+ if ((status & (SSQ_DELAY_RANDOM|SSQ_DELAY))!= 0)
+ scsi_sleep(ahc->our_id*HZ/10);
+ break;
+ }
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV REB reties exhausted\n");
+ }
+#endif
+ /* FALLTHROUGH */
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_SU:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_BUSY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if (targ->dv_state_retry < 60) {
+ if ((status & SSQ_DELAY) != 0)
+ scsi_sleep(1 * HZ);
+ } else {
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV BUSY reties exhausted\n");
+ }
+#endif
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ }
+ break;
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ default:
+ printf("%s: Invalid DV completion state %d\n", ahc_name(ahc),
+ targ->dv_state);
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+}
+
+static uint32_t
+aic_dv_error_action(struct scsi_cmnd *cmd, struct scsi_inquiry_data *inq_data)
+{
+ aic_sense_action err_action;
+ cam_status status;
+ u_int scsi_status;
+ int sense;
+
+ status = ahc_cmd_get_transaction_status(cmd);
+ scsi_status = ahc_cmd_get_scsi_status(cmd);
+ sense = (cmd->result >> 24) == DRIVER_SENSE;
+
+ switch (status) {
+ case CAM_REQ_CMP:
+ err_action = SS_NOP;
+ break;
+ case CAM_AUTOSENSE_FAIL:
+ case CAM_SCSI_STATUS_ERROR:
+
+ switch (scsi_status) {
+ case SCSI_STATUS_OK:
+ case SCSI_STATUS_COND_MET:
+ case SCSI_STATUS_INTERMED:
+ case SCSI_STATUS_INTERMED_COND_MET:
+ err_action = SS_NOP;
+ break;
+ case SCSI_STATUS_CMD_TERMINATED:
+ case SCSI_STATUS_CHECK_COND:
+ if (sense != 0) {
+ struct scsi_sense_data *sense;
+
+ sense = (struct scsi_sense_data *)
+ &cmd->sense_buffer;
+ err_action =
+ aic_sense_error_action(sense, inq_data, 0);
+
+ } else {
+ err_action = SS_RETRY|SSQ_FALLBACK
+ | SSQ_DECREMENT_COUNT|EIO;
+ }
+ break;
+ case SCSI_STATUS_QUEUE_FULL:
+ case SCSI_STATUS_BUSY:
+ err_action = SS_RETRY|SSQ_DELAY|SSQ_MANY
+ | SSQ_DECREMENT_COUNT|EBUSY;
+ break;
+ case SCSI_STATUS_RESERV_CONFLICT:
+ default:
+ err_action = SS_FAIL|EBUSY;
+ break;
+ }
+ break;
+ case CAM_REQ_CMP_ERR:
+ case CAM_CMD_TIMEOUT:
+ case CAM_UNEXP_BUSFREE:
+ case CAM_UNCOR_PARITY:
+ case CAM_DATA_RUN_ERR:
+ err_action = SS_RETRY|SSQ_FALLBACK|EIO;
+ break;
+ case CAM_UA_ABORT:
+ case CAM_UA_TERMIO:
+ case CAM_MSG_REJECT_REC:
+ case CAM_SEL_TIMEOUT:
+ err_action = SS_FAIL|EIO;
+ break;
+ case CAM_REQ_INVALID:
+ case CAM_PATH_INVALID:
+ case CAM_DEV_NOT_THERE:
+ case CAM_NO_HBA:
+ case CAM_PROVIDE_FAIL:
+ case CAM_REQ_TOO_BIG:
+ case CAM_RESRC_UNAVAIL:
+ case CAM_BUSY:
+ default:
+ /* panic?? These should never occur in our application. */
+ err_action = SS_FAIL|EIO;
+ break;
+ case CAM_SCSI_BUS_RESET:
+ case CAM_BDR_SENT:
+ case CAM_REQUEUE_REQ:
+ /* Unconditional requeue */
+ err_action = SS_RETRY;
+ break;
+ }
+
+ return (err_action);
+}
+
+static void
+ahc_linux_dv_fill_cmd(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo)
+{
+ memset(cmd, 0, sizeof(struct scsi_cmnd));
+ cmd->host = ahc->platform_data->host;
+ cmd->scsi_done = ahc_linux_dv_complete;
+ cmd->target = devinfo->target;
+ cmd->lun = devinfo->lun;
+ cmd->channel = devinfo->channel - 'A';
+}
+
+/*
+ * Synthesize an inquiry command. On the return trip, it'll be
+ * sniffed and the device transfer settings set for us.
+ */
+static void
+ahc_linux_dv_inq(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo, struct ahc_linux_target *targ,
+ u_int request_length)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending INQ\n");
+ }
+#endif
+ if (targ->inq_data == NULL)
+ targ->inq_data = malloc(AHC_LINUX_DV_INQ_LEN,
+ M_DEVBUF, M_WAITOK);
+ if (targ->dv_state > AHC_DV_STATE_INQ_ASYNC) {
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(AHC_LINUX_DV_INQ_LEN,
+ M_DEVBUF, M_WAITOK);
+ }
+
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = INQUIRY;
+ cmd->cmnd[4] = request_length;
+ cmd->request_bufflen = request_length;
+ if (targ->dv_state > AHC_DV_STATE_INQ_ASYNC)
+ cmd->request_buffer = targ->dv_buffer;
+ else
+ cmd->request_buffer = targ->inq_data;
+ memset(cmd->request_buffer, 0, AHC_LINUX_DV_INQ_LEN);
+}
+
+static void
+ahc_linux_dv_tur(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending TUR\n");
+ }
+#endif
+ /* Do a TUR to clear out any non-fatal transitional state */
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_NONE;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = TEST_UNIT_READY;
+}
+
+#define AHC_REBD_LEN 4
+
+static void
+ahc_linux_dv_rebd(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo, struct ahc_linux_target *targ)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending REBD\n");
+ }
+#endif
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(AHC_REBD_LEN, M_DEVBUF, M_WAITOK);
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = READ_BUFFER;
+ cmd->cmnd[1] = 0x0b;
+ scsi_ulto3b(AHC_REBD_LEN, &cmd->cmnd[6]);
+ cmd->request_bufflen = AHC_REBD_LEN;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer;
+}
+
+static void
+ahc_linux_dv_web(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo, struct ahc_linux_target *targ)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending WEB\n");
+ }
+#endif
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_WRITE;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = WRITE_BUFFER;
+ cmd->cmnd[1] = 0x0a;
+ scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]);
+ cmd->request_bufflen = targ->dv_echo_size;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer;
+}
+
+static void
+ahc_linux_dv_reb(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo, struct ahc_linux_target *targ)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending REB\n");
+ }
+#endif
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = READ_BUFFER;
+ cmd->cmnd[1] = 0x0a;
+ scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]);
+ cmd->request_bufflen = targ->dv_echo_size;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer1;
+}
+
+static void
+ahc_linux_dv_su(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ)
+{
+ u_int le;
+
+ le = SID_IS_REMOVABLE(targ->inq_data) ? SSS_LOEJ : 0;
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending SU\n");
+ }
+#endif
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_NONE;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = START_STOP_UNIT;
+ cmd->cmnd[4] = le | SSS_START;
+}
+
+/*
+ * Return speed in KB/s.
+ */
+static u_int
+ahc_linux_calc_speed(u_int width, u_int period, u_int offset)
+{
+ u_int freq;
+
+ if (offset != 0)
+ freq = aic_calc_syncsrate(period);
+ else
+ /* Roughly 3.3MB/s for async */
+ freq = 3300;
+ freq <<= width;
+ return (freq);
+}
+
+static int
+ahc_linux_fallback(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ struct ahc_linux_target *targ;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_transinfo *goal;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_syncrate *syncrate;
+ u_long s;
+ u_int width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+ u_int cur_speed;
+ u_int wide_speed;
+ u_int narrow_speed;
+ u_int fallback_speed;
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Trying to fallback\n");
+ }
+#endif
+ ahc_lock(ahc, &s);
+ targ = ahc->platform_data->targets[devinfo->target_offset];
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ goal = &tinfo->goal;
+ width = goal->width;
+ period = goal->period;
+ offset = goal->offset;
+ ppr_options = goal->ppr_options;
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
+ if (targ->dv_next_narrow_period == 0)
+ targ->dv_next_narrow_period = MAX(period, AHC_SYNCRATE_ULTRA2);
+ if (targ->dv_next_wide_period == 0)
+ targ->dv_next_wide_period = period;
+ if (targ->dv_max_ppr_options == 0)
+ targ->dv_max_ppr_options = ppr_options;
+ if (targ->dv_last_ppr_options == 0)
+ targ->dv_last_ppr_options = ppr_options;
+
+ cur_speed = ahc_linux_calc_speed(width, period, offset);
+ wide_speed = ahc_linux_calc_speed(MSG_EXT_WDTR_BUS_16_BIT,
+ targ->dv_next_wide_period,
+ MAX_OFFSET);
+ narrow_speed = ahc_linux_calc_speed(MSG_EXT_WDTR_BUS_8_BIT,
+ targ->dv_next_narrow_period,
+ MAX_OFFSET);
+ fallback_speed = ahc_linux_calc_speed(width, period+1, offset);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ printf("cur_speed= %d, wide_speed= %d, narrow_speed= %d, "
+ "fallback_speed= %d\n", cur_speed, wide_speed,
+ narrow_speed, fallback_speed);
+ }
+#endif
+
+ if (cur_speed > 160000) {
+ /*
+ * Paced/DT/IU_REQ only transfer speeds. All we
+ * can do is fallback in terms of syncrate.
+ */
+ period++;
+ } else if (cur_speed > 80000) {
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ /*
+ * Try without IU_REQ as it may be confusing
+ * an expander.
+ */
+ ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+ } else {
+ /*
+ * Paced/DT only transfer speeds. All we
+ * can do is fallback in terms of syncrate.
+ */
+ period++;
+ ppr_options = targ->dv_max_ppr_options;
+ }
+ } else if (cur_speed > 3300) {
+
+ /*
+ * In this range we the following
+ * options ordered from highest to
+ * lowest desireability:
+ *
+ * o Wide/DT
+ * o Wide/non-DT
+ * o Narrow at a potentally higher sync rate.
+ *
+ * All modes are tested with and without IU_REQ
+ * set since using IUs may confuse an expander.
+ */
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+
+ ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+ } else if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+ /*
+ * Try going non-DT.
+ */
+ ppr_options = targ->dv_max_ppr_options;
+ ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ } else if (targ->dv_last_ppr_options != 0) {
+ /*
+ * Try without QAS or any other PPR options.
+ * We may need a non-PPR message to work with
+ * an expander. We look at the "last PPR options"
+ * so we will perform this fallback even if the
+ * target responded to our PPR negotiation with
+ * no option bits set.
+ */
+ ppr_options = 0;
+ } else if (width == MSG_EXT_WDTR_BUS_16_BIT) {
+ /*
+ * If the next narrow speed is greater than
+ * the next wide speed, fallback to narrow.
+ * Otherwise fallback to the next DT/Wide setting.
+ * The narrow async speed will always be smaller
+ * than the wide async speed, so handle this case
+ * specifically.
+ */
+ ppr_options = targ->dv_max_ppr_options;
+ if (narrow_speed > fallback_speed
+ || period >= AHC_ASYNC_XFER_PERIOD) {
+ targ->dv_next_wide_period = period+1;
+ width = MSG_EXT_WDTR_BUS_8_BIT;
+ period = targ->dv_next_narrow_period;
+ } else {
+ period++;
+ }
+ } else if ((ahc->features & AHC_WIDE) != 0
+ && tinfo->user.width != 0
+ && wide_speed >= fallback_speed
+ && (targ->dv_next_wide_period <= AHC_ASYNC_XFER_PERIOD
+ || period >= AHC_ASYNC_XFER_PERIOD)) {
+
+ /*
+ * We are narrow. Try falling back
+ * to the next wide speed with
+ * all supported ppr options set.
+ */
+ targ->dv_next_narrow_period = period+1;
+ width = MSG_EXT_WDTR_BUS_16_BIT;
+ period = targ->dv_next_wide_period;
+ ppr_options = targ->dv_max_ppr_options;
+ } else {
+ /* Only narrow fallback is allowed. */
+ period++;
+ ppr_options = targ->dv_max_ppr_options;
+ }
+ } else {
+ ahc_unlock(ahc, &s);
+ return (-1);
+ }
+ offset = MAX_OFFSET;
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ AHC_SYNCRATE_DT);
+ ahc_set_width(ahc, devinfo, width, AHC_TRANS_GOAL, FALSE);
+ if (period == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ if (width == MSG_EXT_WDTR_BUS_8_BIT)
+ targ->dv_next_narrow_period = AHC_ASYNC_XFER_PERIOD;
+ else
+ targ->dv_next_wide_period = AHC_ASYNC_XFER_PERIOD;
+ }
+ ahc_set_syncrate(ahc, devinfo, syncrate, period, offset,
+ ppr_options, AHC_TRANS_GOAL, FALSE);
+ targ->dv_last_ppr_options = ppr_options;
+ ahc_unlock(ahc, &s);
+ return (0);
+}
+
+static void
+ahc_linux_dv_timeout(struct scsi_cmnd *cmd)
+{
+ struct ahc_softc *ahc;
+ struct ahc_cmd *acmd;
+ struct ahc_linux_device *next_dev;
+ struct scb *scb;
+ u_long flags;
+
+ ahc = *((struct ahc_softc **)cmd->host->hostdata);
+ ahc_lock(ahc, &flags);
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("%s: Timeout while doing DV command %x.\n",
+ ahc_name(ahc), cmd->cmnd[0]);
+#endif
+
+ /*
+ * Guard against "done race". No action is
+ * required if we just completed.
+ */
+ if ((scb = (struct scb *)cmd->host_scribble) == NULL) {
+ ahc_unlock(ahc, &flags);
+ return;
+ }
+
+ /*
+ * Command has not completed. Mark this
+ * SCB as having failing status prior to
+ * resetting the bus, so we get the correct
+ * error code.
+ */
+ if ((scb->flags & SCB_SENSE) != 0)
+ ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ else
+ ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ ahc_reset_channel(ahc, cmd->channel + 'A', /*initiate*/TRUE);
+
+ /*
+ * Add a minimal bus settle delay for devices that are slow to
+ * respond after bus resets.
+ */
+ ahc_linux_freeze_simq(ahc);
+ init_timer(&ahc->platform_data->reset_timer);
+ ahc->platform_data->reset_timer.data = (u_long)ahc;
+ ahc->platform_data->reset_timer.expires = jiffies + HZ / 2;
+ ahc->platform_data->reset_timer.function =
+ (ahc_linux_callback_t *)ahc_linux_release_simq;
+ add_timer(&ahc->platform_data->reset_timer);
+ /*
+ * In 2.5.X, the "done lock" is the ahc_lock.
+ * Instead of dropping and re-acquiring the same
+ * lock in the 2.5.X case, just hold our ahc_lock
+ * the whole time. ahc_done_lock() has been
+ * made a no-op for 2.5.X too.
+ */
+ acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+ TAILQ_INIT(&ahc->platform_data->completeq);
+ next_dev = ahc_linux_next_device_to_run(ahc);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &flags);
+#endif
+ if (next_dev)
+ ahc_schedule_runq(ahc);
+ if (acmd != NULL) {
+ acmd = ahc_linux_run_complete_queue(ahc, acmd);
+ if (acmd != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_lock(ahc, &flags);
+#endif
+ ahc_schedule_completeq(ahc, acmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &flags);
+#endif
+ }
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &flags);
+#endif
+}
+
+static void
+ahc_linux_dv_complete(struct scsi_cmnd *cmd)
+{
+ struct ahc_softc *ahc;
+
+ ahc = *((struct ahc_softc **)cmd->host->hostdata);
+
+ /* Delete the DV timer before it goes off! */
+ scsi_delete_timer(cmd);
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("%s:%d:%d: Command completed, status= 0x%x\n",
+ ahc_name(ahc), cmd->channel, cmd->target, cmd->result);
+#endif
+
+ /* Wake up the state machine */
+ up(&ahc->platform_data->dv_cmd_sem);
+}
+
+static void
+ahc_linux_generate_dv_pattern(struct ahc_linux_target *targ)
+{
+ uint16_t b;
+ u_int i;
+ u_int j;
+
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK);
+
+ i = 0;
+ b = 0x0001;
+ for (j = 0 ; i < targ->dv_echo_size; j++) {
+ if (j < 32) {
+ /*
+ * 32bytes of sequential numbers.
+ */
+ targ->dv_buffer[i++] = j & 0xff;
+ } else if (j < 48) {
+ /*
+ * 32bytes of repeating 0x0000, 0xffff.
+ */
+ targ->dv_buffer[i++] = (j & 0x02) ? 0xff : 0x00;
+ } else if (j < 64) {
+ /*
+ * 32bytes of repeating 0x5555, 0xaaaa.
+ */
+ targ->dv_buffer[i++] = (j & 0x02) ? 0xaa : 0x55;
+ } else {
+ /*
+ * Remaining buffer is filled with a repeating
+ * patter of:
+ *
+ * 0xffff
+ * ~0x0001 << shifted once in each loop.
+ */
+ if (j & 0x02) {
+ if (j & 0x01) {
+ targ->dv_buffer[i++] = ~(b >> 8) & 0xff;
+ b <<= 1;
+ if (b == 0x0000)
+ b = 0x0001;
+ } else {
+ targ->dv_buffer[i++] = (~b & 0xff);
+ }
+ } else {
+ targ->dv_buffer[i++] = 0xff;
+ }
+ }
+ }
+}
+
+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);
+}
+
+static u_int
+ahc_linux_user_dv_setting(struct ahc_softc *ahc)
+{
+ static int warned_user;
+ int dv;
+
+ if (warned_user == 0
+ && ahc->unit >= NUM_ELEMENTS(aic7xxx_dv_settings)) {
+
+ printf("aic7xxx: WARNING, insufficient "
+ "dv settings instances for installed "
+ "controllers. Using defaults\n");
+ printf("aic7xxx: Please update the "
+ "aic7xxx_dv_settings array in the "
+ "aic7xxx.c source file.\n");
+ dv = -1;
+ warned_user++;
+ } else {
+
+ dv = aic7xxx_dv_settings[ahc->unit];
+ }
+
+ if (dv < 0) {
+ u_long s;
+
+ /*
+ * Apply the default.
+ */
+ /*
+ * XXX - Enable DV on non-U160 controllers once it
+ * has been tested there.
+ */
+ ahc_lock(ahc, &s);
+ dv = (ahc->features & AHC_DT);
+ if (ahc->seep_config != 0
+ && ahc->seep_config->signature >= CFSIGNATURE2)
+ dv = (ahc->seep_config->adapter_control & CFENABLEDV);
+ ahc_unlock(ahc, &s);
+ }
+ return (dv);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahc_linux_device_queue_depth(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev)
+{
+ struct ahc_devinfo devinfo;
+ u_int tags;
+
+ ahc_compile_devinfo(&devinfo,
+ dev->target->channel == 0
+ ? ahc->our_id : ahc->our_id_b,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+ tags = ahc_linux_user_tagdepth(ahc, &devinfo);
+ if (tags != 0
+ && dev->scsi_device != NULL
+ && dev->scsi_device->tagged_supported != 0) {
+
+ 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 {
+ ahc_set_tags(ahc, &devinfo, AHC_QUEUE_NONE);
+ }
+}
+
+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
+ && AHC_DV_SIMQ_FROZEN(ahc) == 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 (AHC_DV_CMD(cmd) != 0)
+ scb->flags |= SCB_SILENT;
+
+ 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ int msg_bytes;
+ uint8_t tag_msgs[2];
+
+ msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs);
+ if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) {
+ hscb->control |= tag_msgs[0];
+ if (tag_msgs[0] == MSG_ORDERED_TASK)
+ dev->commands_since_idle_or_otag = 0;
+ } else
+#endif
+ 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);
+ /*
+ * In 2.5.X, the "done lock" is the ahc_lock.
+ * Instead of dropping and re-acquiring the same
+ * lock in the 2.5.X case, just hold our ahc_lock
+ * the whole time. ahc_done_lock() has been
+ * made a no-op for 2.5.X too.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &flags);
+#endif
+ if (next_dev)
+ ahc_schedule_runq(ahc);
+ if (acmd != NULL) {
+ acmd = ahc_linux_run_complete_queue(ahc, acmd);
+ if (acmd != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_lock(ahc, &flags);
+#endif
+ ahc_schedule_completeq(ahc, acmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &flags);
+#endif
+ }
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &flags);
+#endif
+}
+
+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);
+ while (acmd != NULL)
+ acmd = 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;
+
+ target_offset = target;
+ if (channel != 0)
+ target_offset += 8;
+ /*
+ * Never allow allocation of a target object for
+ * our own SCSIID.
+ */
+ if ((channel == 0 && target == ahc->our_id)
+ || (channel == 1 && target == ahc->our_id_b)) {
+ ahc->platform_data->targets[target_offset] = NULL;
+ return (NULL);
+ }
+
+ 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;
+ targ->flags = AHC_DV_REQUIRED;
+ ahc->platform_data->targets[target_offset] = targ;
+ return (targ);
+}
+
+static void
+ahc_linux_free_target(struct ahc_softc *ahc, struct ahc_linux_target *targ)
+{
+ struct ahc_devinfo devinfo;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_offset;
+ char channel;
+
+ /*
+ * Force a negotiation to async/narrow on any
+ * future command to this device unless a bus
+ * reset occurs between now and that command.
+ */
+ channel = 'A' + targ->channel;
+ our_id = ahc->our_id;
+ target_offset = targ->target;
+ if (targ->channel != 0) {
+ target_offset += 8;
+ our_id = ahc->our_id_b;
+ }
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+ targ->target, &tstate);
+ ahc_compile_devinfo(&devinfo, our_id, targ->target, CAM_LUN_WILDCARD,
+ channel, ROLE_INITIATOR);
+ ahc_set_syncrate(ahc, &devinfo, NULL, 0, 0, 0,
+ AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_update_neg_request(ahc, &devinfo, tstate, tinfo, AHC_NEG_ALWAYS);
+ ahc->platform_data->targets[target_offset] = NULL;
+ if (targ->inq_data != NULL)
+ free(targ->inq_data, M_DEVBUF);
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ 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
+ && (targ->flags & AHC_DV_REQUIRED) == 0)
+ ahc_linux_free_target(ahc, targ);
+}
+
+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++;
+ if ((cmd->result & (CAM_DEV_QFRZN << 16)) != 0) {
+ cmd->result &= ~(CAM_DEV_QFRZN << 16);
+ dev->qfrozen--;
+ }
+ ahc_linux_unmap_scb(ahc, scb);
+
+ /*
+ * 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 ((scb->flags & SCB_TRANSMISSION_ERROR) != 0) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MISC) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("Set CAM_UNCOR_PARITY\n");
+ }
+#endif
+ ahc_set_transaction_status(scb, CAM_UNCOR_PARITY);
+ } else 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);
+ }
+ } else if (ahc_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) {
+ ahc_linux_handle_scsi_status(ahc, dev, scb);
+ } else if (ahc_get_transaction_status(scb) == CAM_SEL_TIMEOUT) {
+ dev->flags |= AHC_DEV_UNCONFIGURED;
+ if (AHC_DV_CMD(cmd) == FALSE)
+ dev->target->flags &= ~AHC_DV_REQUIRED;
+ }
+ /*
+ * Start DV for devices that require it assuming the first command
+ * sent does not result in a selection timeout.
+ */
+ if (ahc_get_transaction_status(scb) != CAM_SEL_TIMEOUT
+ && (dev->target->flags & AHC_DV_REQUIRED) != 0)
+ ahc_linux_start_dv(ahc);
+
+ 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 (ahc_get_transaction_status(scb) == CAM_BDR_SENT
+ || ahc_get_transaction_status(scb) == CAM_REQ_ABORTED)
+ ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ if ((scb->platform_data->flags & AHC_UP_EH_SEMAPHORE) != 0) {
+ scb->platform_data->flags &= ~AHC_UP_EH_SEMAPHORE;
+ up(&ahc->platform_data->eh_sem);
+ }
+ }
+
+ ahc_free_scb(ahc, scb);
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+
+ if ((ahc->platform_data->flags & AHC_DV_WAIT_SIMQ_EMPTY) != 0
+ && LIST_FIRST(&ahc->pending_scbs) == NULL) {
+ ahc->platform_data->flags &= ~AHC_DV_WAIT_SIMQ_EMPTY;
+ up(&ahc->platform_data->dv_sem);
+ }
+
+}
+
+static void
+ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev, struct scb *scb)
+{
+ struct ahc_devinfo devinfo;
+
+ ahc_compile_devinfo(&devinfo,
+ ahc->our_id,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+
+ /*
+ * 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_CHECK_COND:
+ case SCSI_STATUS_CMD_TERMINATED:
+ {
+ Scsi_Cmnd *cmd;
+
+ /*
+ * Copy sense information to the OS's cmd
+ * structure if it is available.
+ */
+ cmd = scb->io_ctx;
+ if (scb->flags & SCB_SENSE) {
+ u_int sense_size;
+
+ sense_size = MIN(sizeof(struct scsi_sense_data)
+ - ahc_get_sense_residual(scb),
+ sizeof(cmd->sense_buffer));
+ memcpy(cmd->sense_buffer,
+ ahc_get_sense_buf(ahc, scb), sense_size);
+ if (sense_size < sizeof(cmd->sense_buffer))
+ memset(&cmd->sense_buffer[sense_size], 0,
+ sizeof(cmd->sense_buffer) - sense_size);
+ cmd->result |= (DRIVER_SENSE << 24);
+ }
+ 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);
+ ahc_set_tags(ahc, &devinfo,
+ (dev->flags & AHC_DEV_Q_BASIC)
+ ? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);
+ break;
+ }
+ /*
+ * Drop down to a single opening, and treat this
+ * as if the target returned BUSY SCSI status.
+ */
+ dev->openings = 1;
+ ahc_set_scsi_status(scb, SCSI_STATUS_BUSY);
+ ahc_set_tags(ahc, &devinfo,
+ (dev->flags & AHC_DEV_Q_BASIC)
+ ? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);
+ /* 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_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;
+
+ /*
+ * Map CAM error codes into Linux Error codes. We
+ * avoid the conversion so that the DV code has the
+ * full error information available when making
+ * state change decisions.
+ */
+ if (AHC_DV_CMD(cmd) == FALSE) {
+ u_int new_status;
+
+ switch (ahc_cmd_get_transaction_status(cmd)) {
+ case CAM_REQ_INPROG:
+ case CAM_REQ_CMP:
+ case CAM_SCSI_STATUS_ERROR:
+ new_status = DID_OK;
+ break;
+ case CAM_REQ_ABORTED:
+ new_status = DID_ABORT;
+ break;
+ case CAM_BUSY:
+ new_status = DID_BUS_BUSY;
+ break;
+ case CAM_REQ_INVALID:
+ case CAM_PATH_INVALID:
+ new_status = DID_BAD_TARGET;
+ break;
+ case CAM_SEL_TIMEOUT:
+ new_status = DID_NO_CONNECT;
+ break;
+ case CAM_SCSI_BUS_RESET:
+ case CAM_BDR_SENT:
+ new_status = DID_RESET;
+ break;
+ case CAM_UNCOR_PARITY:
+ new_status = DID_PARITY;
+ break;
+ case CAM_CMD_TIMEOUT:
+ new_status = DID_TIME_OUT;
+ break;
+ case CAM_UA_ABORT:
+ case CAM_REQ_CMP_ERR:
+ case CAM_AUTOSENSE_FAIL:
+ case CAM_NO_HBA:
+ case CAM_DATA_RUN_ERR:
+ case CAM_UNEXP_BUSFREE:
+ case CAM_SEQUENCE_FAIL:
+ case CAM_CCB_LEN_ERR:
+ case CAM_PROVIDE_FAIL:
+ case CAM_REQ_TERMIO:
+ case CAM_UNREC_HBA_ERROR:
+ case CAM_REQ_TOO_BIG:
+ new_status = DID_ERROR;
+ break;
+ case CAM_REQUEUE_REQ:
+ /*
+ * 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. We also "fake" a check condition with
+ * a sense code of ABORTED COMMAND. This seems to
+ * evoke a retry even if this command is being sent
+ * via the eh thread. Ick! Ick! Ick!
+ */
+ cmd->retries--;
+ new_status = DID_OK;
+ ahc_cmd_set_scsi_status(cmd, SCSI_STATUS_CHECK_COND);
+ cmd->result |= (DRIVER_SENSE << 24);
+ memset(cmd->sense_buffer, 0,
+ sizeof(cmd->sense_buffer));
+ cmd->sense_buffer[0] = SSD_ERRCODE_VALID
+ | SSD_CURRENT_ERROR;
+ cmd->sense_buffer[2] = SSD_KEY_ABORTED_COMMAND;
+ break;
+ default:
+ /* We should never get here */
+ new_status = DID_ERROR;
+ break;
+ }
+
+ ahc_cmd_set_transaction_status(cmd, new_status);
+ }
+
+ 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 void
+ahc_linux_filter_inquiry(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ 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 maxsync;
+ u_int width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+ u_int trans_version;
+ u_int prot_version;
+
+ /*
+ * 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, devinfo->channel - 'A',
+ devinfo->target, devinfo->lun,
+ /*alloc*/TRUE);
+
+ sid = (struct scsi_inquiry_data *)dev->target->inq_data;
+ if (SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
+
+ dev->flags &= ~AHC_DEV_UNCONFIGURED;
+ } else {
+ dev->flags |= AHC_DEV_UNCONFIGURED;
+ return;
+ }
+
+ /*
+ * Update our notion of this device's transfer
+ * negotiation capabilities.
+ */
+ 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;
+ trans_version = user->transport_version;
+ prot_version = MIN(user->protocol_version, SID_ANSI_REV(sid));
+
+ /*
+ * Only attempt SPI3/4 once we've verified that
+ * the device claims to support SPI3/4 features.
+ */
+ if (prot_version < SCSI_REV_2)
+ trans_version = SID_ANSI_REV(sid);
+ else
+ trans_version = SCSI_REV_2;
+
+ 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;
+ }
+ if ((sid->spi3data & SID_SPI_QAS) == 0)
+ ppr_options &= ~MSG_EXT_PPR_QAS_REQ;
+ if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0)
+ ppr_options &= MSG_EXT_PPR_QAS_REQ;
+ if ((sid->spi3data & SID_SPI_IUS) == 0)
+ ppr_options &= (MSG_EXT_PPR_DT_REQ
+ | MSG_EXT_PPR_QAS_REQ);
+
+ if (prot_version > SCSI_REV_2
+ && ppr_options != 0)
+ trans_version = user->transport_version;
+
+ 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. */
+ curr->transport_version = trans_version;
+ curr->protocol_version = prot_version;
+ 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);
+}
+
+static void
+ahc_linux_sem_timeout(u_long arg)
+{
+ struct ahc_softc *ahc;
+ u_long s;
+
+ ahc = (struct ahc_softc *)arg;
+
+ ahc_lock(ahc, &s);
+ if ((ahc->platform_data->flags & AHC_UP_EH_SEMAPHORE) != 0) {
+ ahc->platform_data->flags &= ~AHC_UP_EH_SEMAPHORE;
+ up(&ahc->platform_data->eh_sem);
+ }
+ ahc_unlock(ahc, &s);
+}
+
+static void
+ahc_linux_freeze_simq(struct ahc_softc *ahc)
+{
+ ahc->platform_data->qfrozen++;
+ if (ahc->platform_data->qfrozen == 1) {
+ scsi_block_requests(ahc->platform_data->host);
+
+ /* XXX What about Twin channels? */
+ ahc_platform_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ);
+ }
+}
+
+static void
+ahc_linux_release_simq(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;
+ if (AHC_DV_SIMQ_FROZEN(ahc)
+ && ((ahc->platform_data->flags & AHC_DV_WAIT_SIMQ_RELEASE) != 0)) {
+ ahc->platform_data->flags &= ~AHC_DV_WAIT_SIMQ_RELEASE;
+ up(&ahc->platform_data->dv_sem);
+ }
+ 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);
+
+ ahc_schedule_runq(ahc);
+}
+
+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;
+
+ pending_scb = NULL;
+ 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");
+
+ /*
+ * In all versions of Linux, we have to work around
+ * a major flaw in how the mid-layer is locked down
+ * if we are to sleep successfully in our error handler
+ * while allowing our interrupt handler to run. Since
+ * the midlayer acquires either the io_request_lock or
+ * our lock prior to calling us, we must use the
+ * spin_unlock_irq() method for unlocking our lock.
+ * This will force interrupts to be enabled on the
+ * current CPU. Since the EH thread should not have
+ * been running with CPU interrupts disabled other than
+ * by acquiring either the io_request_lock or our own
+ * lock, this *should* be safe.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_unlock_irq(&io_request_lock);
+#endif
+ ahc_midlayer_entrypoint_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 we didn't "just" miss
+ * an interrupt that would affect this cmd.
+ */
+ ahc_pause_and_flushwork(ahc);
+ paused = TRUE;
+
+ 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) & NOT_IDENTIFIED) == 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) {
+ struct timer_list timer;
+ int ret;
+
+ pending_scb->platform_data->flags |= AHC_UP_EH_SEMAPHORE;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, &s);
+#else
+ spin_unlock_irq(ahc->platform_data->host->host_lock);
+#endif
+ init_timer(&timer);
+ timer.data = (u_long)ahc;
+ 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;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_lock(ahc, &s);
+#else
+ spin_lock_irq(ahc->platform_data->host->host_lock);
+#endif
+ }
+ acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+ TAILQ_INIT(&ahc->platform_data->completeq);
+ ahc_midlayer_entrypoint_unlock(ahc, &s);
+ if (acmd != NULL) {
+ acmd = ahc_linux_run_complete_queue(ahc, acmd);
+ if (acmd != NULL) {
+ ahc_midlayer_entrypoint_lock(ahc, &s);
+ ahc_schedule_completeq(ahc, acmd);
+ ahc_midlayer_entrypoint_unlock(ahc, &s);
+ }
+ }
+ ahc_schedule_runq(ahc);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_lock_irq(&io_request_lock);
+#endif
+ return (retval);
+}
+
+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);
+ }
+ }
+ }
+}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h
index c05f3347f3ec..65ff6384a9a4 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.h
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h
@@ -18,8 +18,6 @@
* 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_linux.h#72 $
- *
* Copyright (c) 2000-2001 Adaptec Inc.
* All rights reserved.
*
@@ -55,7 +53,7 @@
* 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_linux.h#72 $
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.h#111 $
*
*/
#ifndef _AIC7XXX_LINUX_H_
@@ -65,26 +63,32 @@
#include <linux/blk.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
-#include <linux/interrupt.h>
#include <linux/ioport.h>
-#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/version.h>
+#ifndef AHC_MODVERSION_FILE
+#define __NO_VERSION__
+#endif
#include <linux/module.h>
#include <asm/byteorder.h>
-#include <asm/io.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"
+#define AIC_LIB_PREFIX ahc
+#include "scsi.h"
+#include "hosts.h"
+#include "aiclib.h"
/* Name space conflict with BSD queue macros */
#ifdef LIST_HEAD
@@ -95,6 +99,20 @@
#include "queue.h"
#include "scsi_message.h"
+/*********************************** Debugging ********************************/
+#ifdef CONFIG_AIC7XXX_DEBUG_ENABLE
+#ifdef CONFIG_AIC7XXX_DEBUG_MASK
+#define AHC_DEBUG 1
+#define AHC_DEBUG_OPTS CONFIG_AIC7XXX_DEBUG_MASK
+#else
+/*
+ * Compile in debugging code, but do not enable any printfs.
+ */
+#define AHC_DEBUG 1
+#endif
+/* No debugging code. */
+#endif
+
/************************* Forward Declarations *******************************/
struct ahc_softc;
typedef struct pci_dev *ahc_dev_softc_t;
@@ -134,8 +152,9 @@ typedef Scsi_Cmnd *ahc_io_ctx_t;
/************************* Configuration Data *********************************/
extern int aic7xxx_no_probe;
+extern int aic7xxx_allow_memio;
extern int aic7xxx_detect_complete;
-extern Scsi_Host_Template* aic7xxx_driver_template;
+extern Scsi_Host_Template aic7xxx_driver_template;
/***************************** Bus Space/DMA **********************************/
@@ -236,176 +255,52 @@ int ahc_dmamap_unload(struct ahc_softc *, bus_dma_tag_t, bus_dmamap_t);
*/
#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
+/************************** Timer DataStructures ******************************/
+typedef struct timer_list ahc_timer_t;
-/*
- * 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];
-};
+/********************************** Includes **********************************/
+#if CONFIG_AIC7XXX_REG_PRETTY_PRINT
+#define AIC_DEBUG_REGISTERS 1
+#else
+#define AIC_DEBUG_REGISTERS 0
+#endif
+#include "aic7xxx.h"
-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;
-};
+/***************************** Timer Facilities *******************************/
+#define ahc_timer_init init_timer
+#define ahc_timer_stop del_timer
+typedef void ahc_linux_callback_t (u_long);
+static __inline void ahc_timer_reset(ahc_timer_t *timer, int usec,
+ ahc_callback_t *func, void *arg);
+static __inline void ahc_scb_timer_reset(struct scb *scb, u_int usec);
-struct scsi_inquiry_data
+static __inline void
+ahc_timer_reset(ahc_timer_t *timer, int usec, ahc_callback_t *func, void *arg)
{
- 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_linux_host.h"
+ struct ahc_softc *ahc;
+
+ ahc = (struct ahc_softc *)arg;
+ del_timer(timer);
+ timer->data = (u_long)arg;
+ timer->expires = jiffies + (usec * HZ)/1000000;
+ timer->function = (ahc_linux_callback_t*)func;
+ add_timer(timer);
+}
-/* Core driver definitions */
-#include "aic7xxx.h"
+static __inline void
+ahc_scb_timer_reset(struct scb *scb, u_int usec)
+{
+ mod_timer(&scb->io_ctx->eh_timeout, jiffies + (usec * HZ)/1000000);
+}
-/* SMP support */
+/***************************** 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 AIC7XXX_DRIVER_VERSION "6.2.4"
+#define AIC7XXX_DRIVER_VERSION "6.2.24"
/**************************** Front End Queues ********************************/
/*
@@ -452,8 +347,9 @@ typedef enum {
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;
+ AHC_DEV_PERIODIC_OTAG = 0x40, /* Send OTAG to prevent starvation */
+ AHC_DEV_SLAVE_CONFIGURED = 0x80 /* slave_configure() has been called */
+} ahc_linux_dev_flags;
struct ahc_linux_target;
struct ahc_linux_device {
@@ -464,7 +360,7 @@ struct ahc_linux_device {
* The number of transactions currently
* queued to the device.
*/
- int active;
+ int active;
/*
* The currently allowed number of
@@ -474,18 +370,18 @@ struct ahc_linux_device {
* mode where the device may have more
* than one outstanding active transaction.
*/
- int openings;
+ int openings;
/*
* A positive count indicates that this
* device's queue is halted.
*/
- u_int qfrozen;
+ u_int qfrozen;
/*
* Cumulative command counter.
*/
- u_long commands_issued;
+ u_long commands_issued;
/*
* The number of tagged transactions when
@@ -493,21 +389,26 @@ struct ahc_linux_device {
* that have been successfully received by
* this device since the last QUEUE FULL.
*/
- u_int tag_success_count;
+ u_int tag_success_count;
#define AHC_TAG_SUCCESS_INTERVAL 50
- ahc_dev_flags flags;
+ ahc_linux_dev_flags flags;
+
+ /*
+ * Per device timer.
+ */
+ struct timer_list timer;
/*
* The high limit for the tags variable.
*/
- u_int maxtags;
+ u_int maxtags;
/*
* The computed number of tags outstanding
* at the time of the last QUEUE FULL event.
*/
- u_int tags_on_last_queuefull;
+ u_int tags_on_last_queuefull;
/*
* How many times we have seen a queue full
@@ -515,7 +416,7 @@ struct ahc_linux_device {
* to stop our adaptive queue depth algorithm
* on devices with a fixed number of tags.
*/
- u_int last_queuefull_same_count;
+ u_int last_queuefull_same_count;
#define AHC_LOCK_TAGS_COUNT 50
/*
@@ -527,19 +428,54 @@ struct ahc_linux_device {
* if the AHC_DEV_PERIODIC_OTAG flag is set
* on this device.
*/
- u_int commands_since_idle_or_otag;
+ u_int commands_since_idle_or_otag;
#define AHC_OTAG_THRESH 500
- int lun;
- struct ahc_linux_target *target;
+ int lun;
+ Scsi_Device *scsi_device;
+ struct ahc_linux_target *target;
};
+typedef enum {
+ AHC_DV_REQUIRED = 0x01
+} ahc_linux_targ_flags;
+
+/* DV States */
+typedef enum {
+ AHC_DV_STATE_EXIT = 0,
+ AHC_DV_STATE_INQ_SHORT_ASYNC,
+ AHC_DV_STATE_TUR,
+ AHC_DV_STATE_INQ_ASYNC,
+ AHC_DV_STATE_INQ_ASYNC_VERIFY,
+ AHC_DV_STATE_REBD,
+ AHC_DV_STATE_INQ_VERIFY,
+ AHC_DV_STATE_WEB,
+ AHC_DV_STATE_REB,
+ AHC_DV_STATE_SU,
+ AHC_DV_STATE_BUSY
+} ahc_dv_state;
+
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_linux_device *devices[AHC_NUM_LUNS];
+ int channel;
+ int target;
+ int refcount;
+ struct ahc_transinfo last_tinfo;
+ struct ahc_softc *ahc;
+ ahc_linux_targ_flags flags;
+ struct scsi_inquiry_data *inq_data;
+ /*
+ * The next "fallback" period to use for narrow/wide transfers.
+ */
+ u_int dv_next_narrow_period;
+ u_int dv_next_wide_period;
+ u_int dv_max_ppr_options;
+ u_int dv_last_ppr_options;
+ u_int dv_echo_size;
+ ahc_dv_state dv_state;
+ u_int dv_state_retry;
+ char *dv_buffer;
+ char *dv_buffer1;
};
/********************* Definitions Required by the Core ***********************/
@@ -554,12 +490,19 @@ struct ahc_linux_target {
/*
* Per-SCB OSM storage.
*/
+typedef enum {
+ AHC_UP_EH_SEMAPHORE
+} ahc_linux_scb_flags;
+
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
+ uint32_t sense_resid; /* Auto-Sense residual */
+ ahc_linux_scb_flags flags;
};
/*
@@ -568,7 +511,16 @@ struct scb_platform_data {
* alignment restrictions of the various platforms supported by
* this driver.
*/
+typedef enum {
+ AHC_DV_WAIT_SIMQ_EMPTY = 0x01,
+ AHC_DV_WAIT_SIMQ_RELEASE = 0x02,
+ AHC_DV_ACTIVE = 0x04,
+ AHC_DV_SHUTDOWN = 0x08,
+ AHC_RUN_CMPLT_Q_TIMER = 0x10
+} ahc_linux_softc_flags;
+
TAILQ_HEAD(ahc_completeq, ahc_cmd);
+
struct ahc_platform_data {
/*
* Fields accessed from interrupt context.
@@ -577,14 +529,28 @@ struct ahc_platform_data {
TAILQ_HEAD(, ahc_linux_device) device_runq;
struct ahc_completeq completeq;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+ spinlock_t spin_lock;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ struct tasklet_struct runq_tasklet;
+#endif
u_int qfrozen;
+ pid_t dv_pid;
+ struct timer_list completeq_timer;
struct timer_list reset_timer;
struct semaphore eh_sem;
+ struct semaphore dv_sem;
+ struct semaphore dv_cmd_sem; /* XXX This needs to be in
+ * the target struct
+ */
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;
+ ahc_linux_softc_flags flags;
};
/************************** OS Utility Wrappers *******************************/
@@ -706,56 +672,116 @@ 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 acquisition and release of the above lock in midlayer entry points. */
+static __inline void ahc_midlayer_entrypoint_lock(struct ahc_softc *,
+ unsigned long *flags);
+static __inline void ahc_midlayer_entrypoint_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->host->host_lock);
+ 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->host->host_lock, *flags);
+ 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->host->host_lock, *flags);
+ spin_unlock_irqrestore(&ahc->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahc_midlayer_entrypoint_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ /*
+ * In 2.5.X, the midlayer takes our lock just before
+ * calling us, so avoid locking again.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_lock(ahc, flags);
+#endif
+}
+
+static __inline void
+ahc_midlayer_entrypoint_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ /*
+ * In 2.5.X, the midlayer takes our lock just before
+ * calling us and unlocks when we return, so let it do the unlock.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ ahc_unlock(ahc, flags);
+#endif
}
static __inline void
ahc_done_lockinit(struct ahc_softc *ahc)
{
- /* We don't own the iorequest lock, so we don't initialize it. */
+ /*
+ * In 2.5.X, our own lock is held during completions.
+ * In previous versions, the io_request_lock is used.
+ * In either case, we can't initialize this lock again.
+ */
}
static __inline void
ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
{
- struct Scsi_Host *host = ahc->platform_data->host;
-
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
*flags = 0;
- spin_lock_irqsave(host->host_lock, *flags);
+ spin_lock_irqsave(&io_request_lock, *flags);
+#endif
}
static __inline void
ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
{
- struct Scsi_Host *host = ahc->platform_data->host;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_unlock_irqrestore(&io_request_lock, *flags);
+#endif
+}
- spin_unlock_irqrestore(host->host_lock, *flags);
+static __inline void
+ahc_list_lockinit()
+{
+ spin_lock_init(&ahc_list_spinlock);
}
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+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,93) */
+
+static __inline void
ahc_lockinit(struct ahc_softc *ahc)
{
}
@@ -774,6 +800,7 @@ ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
restore_flags(*flags);
}
+static __inline void
ahc_done_lockinit(struct ahc_softc *ahc)
{
}
@@ -792,6 +819,25 @@ 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 ******************************/
@@ -846,7 +892,8 @@ 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);
+int aic7770_map_registers(struct ahc_softc *ahc,
+ u_int port);
int aic7770_map_int(struct ahc_softc *ahc, u_int irq);
/******************************* PCI Routines *********************************/
@@ -959,38 +1006,99 @@ ahc_flush_device_writes(struct ahc_softc *ahc)
#define pci_unmap_single(pdev, buffer, buflen, direction)
#endif
-/*********************** Transaction Access Wrappers **************************/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+#define ahc_pci_set_dma_mask pci_set_dma_mask
+#else
+/*
+ * Always "return" 0 for success.
+ */
+#define ahc_pci_set_dma_mask(dev_softc, mask) \
+ (((dev_softc)->dma_mask = mask) && 0)
+#endif
+/**************************** Proc FS Support *********************************/
+int ahc_linux_proc_info(char *, char **, off_t, int, int, int);
+
+/*************************** Domain Validation ********************************/
+#define AHC_DV_CMD(cmd) ((cmd)->scsi_done == ahc_linux_dv_complete)
+#define AHC_DV_SIMQ_FROZEN(ahc) \
+ ((((ahc)->platform_data->flags & AHC_DV_ACTIVE) != 0) \
+ && (ahc)->platform_data->qfrozen == 1)
+
+/*********************** Transaction Access Wrappers *************************/
+static __inline void ahc_cmd_set_transaction_status(Scsi_Cmnd *, uint32_t);
static __inline void ahc_set_transaction_status(struct scb *, uint32_t);
+static __inline void ahc_cmd_set_scsi_status(Scsi_Cmnd *, uint32_t);
+static __inline void ahc_set_scsi_status(struct scb *, uint32_t);
+static __inline uint32_t ahc_cmd_get_transaction_status(Scsi_Cmnd *cmd);
+static __inline uint32_t ahc_get_transaction_status(struct scb *);
+static __inline uint32_t ahc_cmd_get_scsi_status(Scsi_Cmnd *cmd);
+static __inline uint32_t ahc_get_scsi_status(struct scb *);
+static __inline void ahc_set_transaction_tag(struct scb *, int, u_int);
+static __inline u_long ahc_get_transfer_length(struct scb *);
+static __inline int ahc_get_transfer_dir(struct scb *);
+static __inline void ahc_set_residual(struct scb *, u_long);
+static __inline void ahc_set_sense_residual(struct scb *scb, u_long resid);
+static __inline u_long ahc_get_residual(struct scb *);
+static __inline u_long ahc_get_sense_residual(struct scb *);
+static __inline int ahc_perform_autosense(struct scb *);
+static __inline uint32_t ahc_get_sense_bufsize(struct ahc_softc *,
+ struct scb *);
+static __inline void ahc_notify_xfer_settings_change(struct ahc_softc *,
+ struct ahc_devinfo *);
+static __inline void ahc_platform_scb_free(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline void ahc_freeze_scb(struct scb *scb);
+
+static __inline
+void ahc_cmd_set_transaction_status(Scsi_Cmnd *cmd, uint32_t status)
+{
+ cmd->result &= ~(CAM_STATUS_MASK << 16);
+ cmd->result |= status << 16;
+}
+
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;
+ ahc_cmd_set_transaction_status(scb->io_ctx,status);
+}
+
+static __inline
+void ahc_cmd_set_scsi_status(Scsi_Cmnd *cmd, uint32_t status)
+{
+ cmd->result &= ~0xFFFF;
+ cmd->result |= status;
}
-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;
+ ahc_cmd_set_scsi_status(scb->io_ctx, status);
+}
+
+static __inline
+uint32_t ahc_cmd_get_transaction_status(Scsi_Cmnd *cmd)
+{
+ return ((cmd->result >> 16) & CAM_STATUS_MASK);
}
-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);
+ return (ahc_cmd_get_transaction_status(scb->io_ctx));
+}
+
+static __inline
+uint32_t ahc_cmd_get_scsi_status(Scsi_Cmnd *cmd)
+{
+ return (cmd->result & 0xFFFF);
}
-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);
+ return (ahc_cmd_get_scsi_status(scb->io_ctx));
}
-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)
{
@@ -1000,14 +1108,12 @@ void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type)
*/
}
-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)
{
@@ -1032,7 +1138,6 @@ int ahc_get_transfer_dir(struct scb *scb)
#endif
}
-static __inline void ahc_set_residual(struct scb *, u_long);
static __inline
void ahc_set_residual(struct scb *scb, u_long resid)
{
@@ -1043,14 +1148,12 @@ void ahc_set_residual(struct scb *scb, u_long 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 */
+ scb->platform_data->sense_resid = resid;
}
-static __inline u_long ahc_get_residual(struct scb *);
static __inline
u_long ahc_get_residual(struct scb *scb)
{
@@ -1061,7 +1164,12 @@ u_long ahc_get_residual(struct scb *scb)
#endif
}
-static __inline int ahc_perform_autosense(struct scb *);
+static __inline
+u_long ahc_get_sense_residual(struct scb *scb)
+{
+ return (scb->platform_data->sense_resid);
+}
+
static __inline
int ahc_perform_autosense(struct scb *scb)
{
@@ -1079,8 +1187,6 @@ 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)
@@ -1088,8 +1194,6 @@ ahc_notify_xfer_settings_change(struct ahc_softc *ahc,
/* 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)
{
@@ -1099,11 +1203,14 @@ ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb)
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 */
+ if ((scb->io_ctx->result & (CAM_DEV_QFRZN << 16)) == 0) {
+ scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
+ scb->platform_data->dev->qfrozen++;
+ }
}
void ahc_platform_set_tags(struct ahc_softc *ahc,
diff --git a/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
index 92f8a86b132f..495367b66a6d 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
@@ -36,7 +36,7 @@
* 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_linux_pci.c#27 $
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c#41 $
*/
#include "aic7xxx_osm.h"
@@ -71,6 +71,7 @@ static struct pci_device_id ahc_linux_pci_id_table[] = {
},
{ 0 }
};
+
MODULE_DEVICE_TABLE(pci, ahc_linux_pci_id_table);
struct pci_driver aic7xxx_pci_driver = {
@@ -84,20 +85,24 @@ static void
ahc_linux_pci_dev_remove(struct pci_dev *pdev)
{
struct ahc_softc *ahc;
- struct ahc_softc *list_ahc;
+ u_long l;
/*
* We should be able to just perform
* the free directly, but check our
* list for extra sanity.
*/
- ahc = pci_get_drvdata(pdev);
- TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
- if (list_ahc == ahc) {
- ahc_free(ahc);
- break;
- }
+ ahc_list_lock(&l);
+ ahc = ahc_find_softc((struct ahc_softc *)pci_get_drvdata(pdev));
+ 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) */
@@ -156,22 +161,14 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
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_pci_set_dma_mask(pdev, 0x7FFFFFFFFFULL) == 0) {
ahc->flags |= AHC_39BIT_ADDRESSING;
ahc->platform_data->hw_dma_mask =
(bus_addr_t)(0x7FFFFFFFFFULL & (bus_addr_t)~0);
}
#endif
ahc->dev_softc = pci;
- ahc->platform_data->irq = pdev->irq;
error = ahc_pci_config(ahc, entry);
if (error != 0) {
ahc_free(ahc);
@@ -180,7 +177,7 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
#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);
+ ahc_linux_register_host(ahc, &aic7xxx_driver_template);
#endif
return (0);
}
@@ -218,13 +215,16 @@ ahc_linux_pci_probe(Scsi_Host_Template *template)
static int
ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc, u_long *base)
{
+ if (aic7xxx_allow_memio == 0)
+ return (ENOMEM);
+
#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)
+ if (*base == 0)
return (ENOMEM);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
if (check_region(*base, 256) != 0)
@@ -268,9 +268,12 @@ ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
#endif
if (error == 0) {
*maddr = ioremap_nocache(base_page, base_offset + 256);
- if (*maddr == NULL)
+ if (*maddr == NULL) {
error = ENOMEM;
- else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ release_mem_region(start, 0x1000);
+#endif
+ } else
*maddr += base_offset;
}
} else
@@ -286,11 +289,9 @@ ahc_pci_map_registers(struct ahc_softc *ahc)
u_long base;
uint8_t *maddr;
int error;
- int io_error;
/*
- * We always reserve both our register spaces to avoid
- * other devices claiming them.
+ * 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);
@@ -309,46 +310,49 @@ ahc_pci_map_registers(struct ahc_softc *ahc)
* Do a quick test to see if memory mapped
* I/O is functioning correctly.
*/
- if (ahc_inb(ahc, HCNTRL) == 0xFF) {
+ if (ahc_pci_test_register_access(ahc) != 0) {
printf("aic7xxx: PCI Device %d:%d:%d "
- "failed memory mapped test\n",
+ "failed memory mapped test. Using PIO.\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 map device.\n",
+ "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
+#endif /* MMAPIO */
/*
- * We always prefer memory mapped access. Only
- * complain about our ioport conflicting with
- * another device if we are going to use it.
+ * We always prefer memory mapped access.
*/
- io_error = ahc_linux_pci_reserve_io_region(ahc, &base);
if (maddr == NULL) {
- error = io_error;
- if (error != 0) {
+
+ 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);
- base = 0;
- } else {
- ahc->tag = BUS_SPACE_PIO;
- ahc->bsh.ioport = base;
- command |= PCIM_CMD_PORTEN;
}
}
ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
@@ -360,9 +364,10 @@ ahc_pci_map_int(struct ahc_softc *ahc)
{
int error;
- ahc->platform_data->irq = ahc->dev_softc->irq;
- error = request_irq(ahc->platform_data->irq, ahc_linux_isr,
+ 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);
}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.c b/drivers/scsi/aic7xxx/aic7xxx_pci.c
index 6a74611d165b..b335373099f9 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_pci.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_pci.c
@@ -39,14 +39,20 @@
* 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#32 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_pci.c#54 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_pci.c,v 1.6 2000/11/10 20:13:41 gibbs Exp $
+ * $FreeBSD$
*/
+#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 */
@@ -207,7 +213,7 @@ ahc_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
: ((id) & 0x1000) >> 12)
/*
* Informational only. Should use chip register to be
- * ceratian, but may be use in identification strings.
+ * certain, but may be use in identification strings.
*/
#define SUBID_9005_CARD_SCSIWIDTH_MASK 0x2000
#define SUBID_9005_CARD_PCIWIDTH_MASK 0x4000
@@ -654,6 +660,14 @@ const u_int ahc_num_pci_devs = NUM_ELEMENTS(ahc_pci_ident_table);
#define CACHESIZE 0x0000003ful /* only 5 bits */
#define LATTIME 0x0000ff00ul
+/* PCI STATUS definitions */
+#define DPE 0x80
+#define SSE 0x40
+#define RMA 0x20
+#define RTA 0x10
+#define STA 0x08
+#define DPR 0x01
+
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);
@@ -661,6 +675,8 @@ 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,
@@ -679,9 +695,6 @@ static void aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
static void aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
int *externalcable_present,
int *eeprom_present);
-static int acquire_seeprom(struct ahc_softc *ahc,
- struct seeprom_descriptor *sd);
-static void release_seeprom(struct seeprom_descriptor *sd);
static void write_brdctl(struct ahc_softc *ahc, uint8_t value);
static uint8_t read_brdctl(struct ahc_softc *ahc);
@@ -766,27 +779,35 @@ ahc_find_pci_device(ahc_dev_softc_t pci)
int
ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry)
{
- struct scb_data *shared_scb_data;
- 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;
+ u_long l;
+ u_int command;
+ u_int our_id;
+ u_int sxfrctl1;
+ u_int scsiseq;
+ u_int dscommand0;
+ int error;
+ uint8_t sblkctl;
+
+ our_id = 0;
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);
- ahc_power_state_change(ahc, AHC_POWER_STATE_D0);
+ /*
+ * 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
@@ -964,19 +985,18 @@ ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry)
return (error);
/*
- * Link this softc in with all other ahc instances.
- */
- ahc_softc_insert(ahc);
-
- /*
* Allow interrupts now that we are completely setup.
*/
error = ahc_pci_map_int(ahc);
if (error != 0)
return (error);
- ahc_intr_enable(ahc, TRUE);
-
+ ahc_list_lock(&l);
+ /*
+ * Link this softc in with all other ahc instances.
+ */
+ ahc_softc_insert(ahc);
+ ahc_list_unlock(&l);
return (0);
}
@@ -999,6 +1019,14 @@ ahc_ext_scbram_present(struct ahc_softc *ahc)
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
@@ -1026,6 +1054,9 @@ ahc_scbram_config(struct ahc_softc *ahc, int enable, int pcheck,
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;
@@ -1165,6 +1196,63 @@ done:
}
/*
+ * Perform some simple tests that should catch situations where
+ * our registers are invalidly mapped.
+ */
+int
+ahc_pci_test_register_access(struct ahc_softc *ahc)
+{
+ int error;
+ u_int status1;
+ uint8_t seqctl;
+
+ error = EIO;
+
+ /* Enable PCI error interrupt status */
+ seqctl = ahc_inb(ahc, SEQCTL);
+ ahc_outb(ahc, SEQCTL, seqctl & ~FAILDIS);
+
+ /*
+ * First a simple test to see if any
+ * registers can be read. Reading
+ * HCNTRL has no side effects and has
+ * at least one bit that is guaranteed to
+ * be zero so it is a good register to
+ * use for this test.
+ */
+ if (ahc_inb(ahc, HCNTRL) == 0xFF)
+ goto fail;
+
+ /*
+ * Next create a situation where write combining
+ * or read prefetching could be initiated by the
+ * CPU or host bridge. Our device does not support
+ * either, so look for data corruption and/or flagged
+ * PCI errors.
+ */
+ ahc_outl(ahc, SRAM_BASE, 0x5aa555aa);
+ if (ahc_inl(ahc, SRAM_BASE) != 0x5aa555aa)
+ goto fail;
+
+ status1 = ahc_pci_read_config(ahc->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+ if ((status1 & STA) != 0)
+ goto fail;
+
+ error = 0;
+
+fail:
+ /* Silently clear any latched errors. */
+ status1 = ahc_pci_read_config(ahc->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+ ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+ status1, /*bytes*/1);
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, SEQCTL, seqctl);
+ return (error);
+}
+
+/*
* Check the external port logic for a serial eeprom
* and termination/cable detection contrls.
*/
@@ -1172,9 +1260,7 @@ static void
check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
{
struct seeprom_descriptor sd;
- struct seeprom_config sc;
- u_int scsi_conf;
- u_int adapter_control;
+ struct seeprom_config *sc;
int have_seeprom;
int have_autoterm;
@@ -1182,6 +1268,7 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
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
@@ -1201,7 +1288,7 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
sd.sd_DO = SEEDO;
sd.sd_DI = SEEDI;
- have_seeprom = acquire_seeprom(ahc, &sd);
+ have_seeprom = ahc_acquire_seeprom(ahc, &sd);
if (have_seeprom) {
if (bootverbose)
@@ -1212,11 +1299,12 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
start_addr = 32 * (ahc->channel - 'A');
- have_seeprom = read_seeprom(&sd, (uint16_t *)&sc,
- start_addr, sizeof(sc)/2);
+ have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)sc,
+ start_addr,
+ sizeof(*sc)/2);
if (have_seeprom)
- have_seeprom = verify_cksum(&sc);
+ have_seeprom = ahc_verify_cksum(sc);
if (have_seeprom != 0 || sd.sd_chip == C56_66) {
if (bootverbose) {
@@ -1229,7 +1317,7 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
}
sd.sd_chip = C56_66;
}
- release_seeprom(&sd);
+ ahc_release_seeprom(&sd);
}
if (!have_seeprom) {
@@ -1248,138 +1336,35 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
uint16_t *sc_data;
int i;
- sc_data = (uint16_t *)&sc;
- for (i = 0; i < 32; i++) {
- uint16_t val;
- int j;
+ sc_data = (uint16_t *)sc;
+ for (i = 0; i < 32; i++, sc_data++) {
+ int j;
j = i * 2;
- val = ahc_inb(ahc, SRAM_BASE + j)
- | ahc_inb(ahc, SRAM_BASE + j + 1) << 8;
+ *sc_data = ahc_inb(ahc, SRAM_BASE + j)
+ | ahc_inb(ahc, SRAM_BASE + j + 1) << 8;
}
- have_seeprom = verify_cksum(&sc);
+ 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 {
- /*
- * Put the data we've collected down into SRAM
- * where ahc_init will find it.
- */
- int i;
- int max_targ = sc.max_targets & CFMAXTARG;
- 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);
+ ahc_parse_pci_eeprom(ahc, sc);
}
/*
@@ -1389,10 +1374,6 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
* hasn't failed yet...
*/
have_autoterm = have_seeprom;
- if (have_seeprom)
- adapter_control = sc.adapter_control;
- else
- adapter_control = CFAUTOTERM;
/*
* Some low-cost chips have SEEPROM and auto-term control built
@@ -1400,20 +1381,144 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
* if the termination logic is enabled.
*/
if ((ahc->features & AHC_SPIOCAP) != 0) {
- if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) != 0)
- have_autoterm = TRUE;
- else
+ if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) == 0)
have_autoterm = FALSE;
}
if (have_autoterm) {
- acquire_seeprom(ahc, &sd);
- configure_termination(ahc, &sd, adapter_control, sxfrctl1);
- release_seeprom(&sd);
+ 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,
@@ -1453,10 +1558,10 @@ configure_termination(struct ahc_softc *ahc,
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);
+ &enableSEC_high,
+ &enablePRI_low,
+ &enablePRI_high,
+ &eeprom_present);
if ((adapter_control & CFSEAUTOTERM) == 0) {
if (bootverbose)
printf("%s: Manual SE Termination\n",
@@ -1480,6 +1585,8 @@ configure_termination(struct ahc_softc *ahc,
aic785X_cable_detect(ahc, &internal50_present,
&externalcable_present,
&eeprom_present);
+ /* Can never support a wide connector. */
+ internal68_present = 0;
} else {
aic787X_cable_detect(ahc, &internal50_present,
&internal68_present,
@@ -1534,6 +1641,15 @@ configure_termination(struct ahc_softc *ahc,
"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
@@ -1696,7 +1812,12 @@ 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);
@@ -1706,8 +1827,8 @@ aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
*eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) ? 1 : 0;
}
-static int
-acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
+int
+ahc_acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
{
int wait;
@@ -1734,8 +1855,8 @@ acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
return(1);
}
-static void
-release_seeprom(struct seeprom_descriptor *sd)
+void
+ahc_release_seeprom(struct seeprom_descriptor *sd)
{
/* Release access to the memory port and the serial EEPROM. */
SEEPROM_OUTB(sd, 0);
@@ -1796,13 +1917,6 @@ read_brdctl(ahc)
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)
{
@@ -1892,10 +2006,8 @@ ahc_aic7860_setup(struct ahc_softc *ahc)
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);
@@ -1906,9 +2018,7 @@ ahc_apa1480_setup(struct ahc_softc *ahc)
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;
@@ -1972,13 +2082,9 @@ ahc_aic7880_setup(struct ahc_softc *ahc)
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);
+ return (ahc_aic7880_setup(ahc));
}
static int
@@ -2023,9 +2129,7 @@ ahc_aic7890_setup(struct ahc_softc *ahc)
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;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_proc.c b/drivers/scsi/aic7xxx/aic7xxx_proc.c
index f4e31195ebe0..8a510e7daec1 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_proc.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_proc.c
@@ -37,20 +37,22 @@
* String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
* sym driver.
*
- * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_proc.c#13 $
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_proc.c#23 $
*/
#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)
@@ -94,47 +96,6 @@ copy_info(struct info_str *info, char *fmt, ...)
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)
{
@@ -145,7 +106,7 @@ ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo)
speed = 3300;
freq = 0;
if (tinfo->offset != 0) {
- freq = scsi_calc_syncsrate(tinfo->period);
+ freq = aic_calc_syncsrate(tinfo->period);
speed = freq;
}
speed *= (0x01 << tinfo->width);
@@ -225,6 +186,104 @@ ahc_dump_device_state(struct info_str *info, struct ahc_linux_device *dev)
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_PCI_CONFIG > 0
+ if ((ahc->chip & AHC_PCI) != 0) {
+ 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);
+ } else
+#endif
+ 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 {
+ printf("ahc_proc_write_seeprom: unsupported adapter type\n");
+ goto done;
+ }
+
+ 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_PCI_CONFIG > 0
+ if ((ahc->chip & AHC_VL) == 0)
+ ahc_release_seeprom(&sd);
+#endif
+ written = length;
+ }
+
+done:
+ if (!paused)
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ return (written);
+}
+
/*
* Return information to handle /proc support for the driver.
*/
@@ -235,20 +294,26 @@ ahc_linux_proc_info(char *buffer, char **start, off_t offset,
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)
- return (-EINVAL);
+ goto done;
/* Has data been written to the file? */
- if (inout == TRUE)
- return (-ENOSYS);
+ if (inout == TRUE) {
+ retval = ahc_proc_write_seeprom(ahc, buffer, length);
+ goto done;
+ }
if (start)
*start = buffer;
@@ -261,7 +326,22 @@ ahc_linux_proc_info(char *buffer, char **start, off_t offset,
copy_info(&info, "Adaptec AIC7xxx driver version: %s\n",
AIC7XXX_DRIVER_VERSION);
ahc_controller_info(ahc, ahc_info);
- copy_info(&info, "%s\n", 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)
@@ -284,5 +364,8 @@ ahc_linux_proc_info(char *buffer, char **start, off_t offset,
ahc_dump_target_state(ahc, &info, our_id,
channel, target_id, i);
}
- return (info.pos > info.offset ? info.pos - info.offset : 0);
+ retval = info.pos > info.offset ? info.pos - info.offset : 0;
+done:
+ ahc_list_unlock(&s);
+ return (retval);
}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
index a81ea7276bf5..bb46dc495ac4 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
+++ b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
@@ -2,9 +2,1073 @@
* DO NOT EDIT - This file is automatically generated
* from the following source files:
*
- * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#37 $
- * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#24 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#52 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#36 $
*/
+typedef int (ahc_reg_print_t)(u_int, u_int *, u_int);
+typedef struct ahc_reg_parse_entry {
+ char *name;
+ uint8_t value;
+ uint8_t mask;
+} ahc_reg_parse_entry_t;
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiseq_print;
+#else
+#define ahc_scsiseq_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSISEQ", 0x00, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sxfrctl0_print;
+#else
+#define ahc_sxfrctl0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SXFRCTL0", 0x01, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sxfrctl1_print;
+#else
+#define ahc_sxfrctl1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SXFRCTL1", 0x02, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsisigo_print;
+#else
+#define ahc_scsisigo_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSISIGO", 0x03, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsisigi_print;
+#else
+#define ahc_scsisigi_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSISIGI", 0x03, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsirate_print;
+#else
+#define ahc_scsirate_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIRATE", 0x04, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiid_print;
+#else
+#define ahc_scsiid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIID", 0x05, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsidatl_print;
+#else
+#define ahc_scsidatl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIDATL", 0x06, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsidath_print;
+#else
+#define ahc_scsidath_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIDATH", 0x07, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_stcnt_print;
+#else
+#define ahc_stcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "STCNT", 0x08, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_optionmode_print;
+#else
+#define ahc_optionmode_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "OPTIONMODE", 0x08, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_targcrccnt_print;
+#else
+#define ahc_targcrccnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "TARGCRCCNT", 0x0a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_clrsint0_print;
+#else
+#define ahc_clrsint0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CLRSINT0", 0x0b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat0_print;
+#else
+#define ahc_sstat0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SSTAT0", 0x0b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_clrsint1_print;
+#else
+#define ahc_clrsint1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CLRSINT1", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat1_print;
+#else
+#define ahc_sstat1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SSTAT1", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat2_print;
+#else
+#define ahc_sstat2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SSTAT2", 0x0d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat3_print;
+#else
+#define ahc_sstat3_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SSTAT3", 0x0e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiid_ultra2_print;
+#else
+#define ahc_scsiid_ultra2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIID_ULTRA2", 0x0f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_simode0_print;
+#else
+#define ahc_simode0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SIMODE0", 0x10, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_simode1_print;
+#else
+#define ahc_simode1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SIMODE1", 0x11, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsibusl_print;
+#else
+#define ahc_scsibusl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIBUSL", 0x12, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsibush_print;
+#else
+#define ahc_scsibush_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIBUSH", 0x13, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sxfrctl2_print;
+#else
+#define ahc_sxfrctl2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SXFRCTL2", 0x13, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_shaddr_print;
+#else
+#define ahc_shaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SHADDR", 0x14, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seltimer_print;
+#else
+#define ahc_seltimer_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SELTIMER", 0x18, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_selid_print;
+#else
+#define ahc_selid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SELID", 0x19, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scamctl_print;
+#else
+#define ahc_scamctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCAMCTL", 0x1a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_targid_print;
+#else
+#define ahc_targid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "TARGID", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_spiocap_print;
+#else
+#define ahc_spiocap_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SPIOCAP", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_brdctl_print;
+#else
+#define ahc_brdctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BRDCTL", 0x1d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seectl_print;
+#else
+#define ahc_seectl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEECTL", 0x1e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sblkctl_print;
+#else
+#define ahc_sblkctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SBLKCTL", 0x1f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_busy_targets_print;
+#else
+#define ahc_busy_targets_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BUSY_TARGETS", 0x20, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ultra_enb_print;
+#else
+#define ahc_ultra_enb_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ULTRA_ENB", 0x30, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_disc_dsb_print;
+#else
+#define ahc_disc_dsb_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DISC_DSB", 0x32, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_cmdsize_table_tail_print;
+#else
+#define ahc_cmdsize_table_tail_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CMDSIZE_TABLE_TAIL", 0x34, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_mwi_residual_print;
+#else
+#define ahc_mwi_residual_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "MWI_RESIDUAL", 0x38, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_next_queued_scb_print;
+#else
+#define ahc_next_queued_scb_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "NEXT_QUEUED_SCB", 0x39, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_msg_out_print;
+#else
+#define ahc_msg_out_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "MSG_OUT", 0x3a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dmaparams_print;
+#else
+#define ahc_dmaparams_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DMAPARAMS", 0x3b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seq_flags_print;
+#else
+#define ahc_seq_flags_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQ_FLAGS", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_saved_scsiid_print;
+#else
+#define ahc_saved_scsiid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SAVED_SCSIID", 0x3d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_saved_lun_print;
+#else
+#define ahc_saved_lun_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SAVED_LUN", 0x3e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_lastphase_print;
+#else
+#define ahc_lastphase_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "LASTPHASE", 0x3f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_waiting_scbh_print;
+#else
+#define ahc_waiting_scbh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "WAITING_SCBH", 0x40, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_disconnected_scbh_print;
+#else
+#define ahc_disconnected_scbh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DISCONNECTED_SCBH", 0x41, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_free_scbh_print;
+#else
+#define ahc_free_scbh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "FREE_SCBH", 0x42, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_complete_scbh_print;
+#else
+#define ahc_complete_scbh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "COMPLETE_SCBH", 0x43, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hscb_addr_print;
+#else
+#define ahc_hscb_addr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HSCB_ADDR", 0x44, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_shared_data_addr_print;
+#else
+#define ahc_shared_data_addr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SHARED_DATA_ADDR", 0x48, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_kernel_qinpos_print;
+#else
+#define ahc_kernel_qinpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "KERNEL_QINPOS", 0x4c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qinpos_print;
+#else
+#define ahc_qinpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QINPOS", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qoutpos_print;
+#else
+#define ahc_qoutpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QOUTPOS", 0x4e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_kernel_tqinpos_print;
+#else
+#define ahc_kernel_tqinpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "KERNEL_TQINPOS", 0x4f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_tqinpos_print;
+#else
+#define ahc_tqinpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "TQINPOS", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_arg_1_print;
+#else
+#define ahc_arg_1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ARG_1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_arg_2_print;
+#else
+#define ahc_arg_2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ARG_2", 0x52, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_last_msg_print;
+#else
+#define ahc_last_msg_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "LAST_MSG", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiseq_template_print;
+#else
+#define ahc_scsiseq_template_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSISEQ_TEMPLATE", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_data_count_odd_print;
+#else
+#define ahc_data_count_odd_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DATA_COUNT_ODD", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ha_274_biosglobal_print;
+#else
+#define ahc_ha_274_biosglobal_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HA_274_BIOSGLOBAL", 0x56, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seq_flags2_print;
+#else
+#define ahc_seq_flags2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQ_FLAGS2", 0x57, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiconf_print;
+#else
+#define ahc_scsiconf_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSICONF", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_intdef_print;
+#else
+#define ahc_intdef_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "INTDEF", 0x5c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hostconf_print;
+#else
+#define ahc_hostconf_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HOSTCONF", 0x5d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ha_274_biosctrl_print;
+#else
+#define ahc_ha_274_biosctrl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HA_274_BIOSCTRL", 0x5f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seqctl_print;
+#else
+#define ahc_seqctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQCTL", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seqram_print;
+#else
+#define ahc_seqram_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQRAM", 0x61, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seqaddr0_print;
+#else
+#define ahc_seqaddr0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQADDR0", 0x62, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seqaddr1_print;
+#else
+#define ahc_seqaddr1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQADDR1", 0x63, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_accum_print;
+#else
+#define ahc_accum_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ACCUM", 0x64, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sindex_print;
+#else
+#define ahc_sindex_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SINDEX", 0x65, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dindex_print;
+#else
+#define ahc_dindex_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DINDEX", 0x66, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_allones_print;
+#else
+#define ahc_allones_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ALLONES", 0x69, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_allzeros_print;
+#else
+#define ahc_allzeros_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ALLZEROS", 0x6a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_none_print;
+#else
+#define ahc_none_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "NONE", 0x6a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_flags_print;
+#else
+#define ahc_flags_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "FLAGS", 0x6b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sindir_print;
+#else
+#define ahc_sindir_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SINDIR", 0x6c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dindir_print;
+#else
+#define ahc_dindir_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DINDIR", 0x6d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_function1_print;
+#else
+#define ahc_function1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "FUNCTION1", 0x6e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_stack_print;
+#else
+#define ahc_stack_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "STACK", 0x6f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_targ_offset_print;
+#else
+#define ahc_targ_offset_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "TARG_OFFSET", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sram_base_print;
+#else
+#define ahc_sram_base_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SRAM_BASE", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_bctl_print;
+#else
+#define ahc_bctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BCTL", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dscommand0_print;
+#else
+#define ahc_dscommand0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DSCOMMAND0", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_bustime_print;
+#else
+#define ahc_bustime_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BUSTIME", 0x85, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dscommand1_print;
+#else
+#define ahc_dscommand1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DSCOMMAND1", 0x85, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_busspd_print;
+#else
+#define ahc_busspd_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BUSSPD", 0x86, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hs_mailbox_print;
+#else
+#define ahc_hs_mailbox_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HS_MAILBOX", 0x86, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dspcistatus_print;
+#else
+#define ahc_dspcistatus_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DSPCISTATUS", 0x86, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hcntrl_print;
+#else
+#define ahc_hcntrl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HCNTRL", 0x87, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_haddr_print;
+#else
+#define ahc_haddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HADDR", 0x88, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hcnt_print;
+#else
+#define ahc_hcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HCNT", 0x8c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scbptr_print;
+#else
+#define ahc_scbptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCBPTR", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_intstat_print;
+#else
+#define ahc_intstat_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "INTSTAT", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_clrint_print;
+#else
+#define ahc_clrint_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CLRINT", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_error_print;
+#else
+#define ahc_error_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ERROR", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfcntrl_print;
+#else
+#define ahc_dfcntrl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFCNTRL", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfstatus_print;
+#else
+#define ahc_dfstatus_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFSTATUS", 0x94, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfwaddr_print;
+#else
+#define ahc_dfwaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFWADDR", 0x95, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfraddr_print;
+#else
+#define ahc_dfraddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFRADDR", 0x97, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfdat_print;
+#else
+#define ahc_dfdat_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFDAT", 0x99, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scbcnt_print;
+#else
+#define ahc_scbcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCBCNT", 0x9a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qinfifo_print;
+#else
+#define ahc_qinfifo_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QINFIFO", 0x9b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qincnt_print;
+#else
+#define ahc_qincnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QINCNT", 0x9c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qoutfifo_print;
+#else
+#define ahc_qoutfifo_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QOUTFIFO", 0x9d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_crccontrol1_print;
+#else
+#define ahc_crccontrol1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CRCCONTROL1", 0x9d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qoutcnt_print;
+#else
+#define ahc_qoutcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QOUTCNT", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiphase_print;
+#else
+#define ahc_scsiphase_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIPHASE", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sfunct_print;
+#else
+#define ahc_sfunct_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SFUNCT", 0x9f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_base_print;
+#else
+#define ahc_scb_base_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_BASE", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_cdb_ptr_print;
+#else
+#define ahc_scb_cdb_ptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_CDB_PTR", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_residual_sgptr_print;
+#else
+#define ahc_scb_residual_sgptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_RESIDUAL_SGPTR", 0xa4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_scsi_status_print;
+#else
+#define ahc_scb_scsi_status_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SCSI_STATUS", 0xa8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_target_phases_print;
+#else
+#define ahc_scb_target_phases_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_TARGET_PHASES", 0xa9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_target_data_dir_print;
+#else
+#define ahc_scb_target_data_dir_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_TARGET_DATA_DIR", 0xaa, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_target_itag_print;
+#else
+#define ahc_scb_target_itag_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_TARGET_ITAG", 0xab, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_dataptr_print;
+#else
+#define ahc_scb_dataptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_DATAPTR", 0xac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_datacnt_print;
+#else
+#define ahc_scb_datacnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_DATACNT", 0xb0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_sgptr_print;
+#else
+#define ahc_scb_sgptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SGPTR", 0xb4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_control_print;
+#else
+#define ahc_scb_control_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_CONTROL", 0xb8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_scsiid_print;
+#else
+#define ahc_scb_scsiid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SCSIID", 0xb9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_lun_print;
+#else
+#define ahc_scb_lun_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_LUN", 0xba, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_tag_print;
+#else
+#define ahc_scb_tag_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_TAG", 0xbb, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_cdb_len_print;
+#else
+#define ahc_scb_cdb_len_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_CDB_LEN", 0xbc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_scsirate_print;
+#else
+#define ahc_scb_scsirate_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SCSIRATE", 0xbd, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_scsioffset_print;
+#else
+#define ahc_scb_scsioffset_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SCSIOFFSET", 0xbe, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_next_print;
+#else
+#define ahc_scb_next_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_NEXT", 0xbf, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_64_spare_print;
+#else
+#define ahc_scb_64_spare_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_64_SPARE", 0xc0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seectl_2840_print;
+#else
+#define ahc_seectl_2840_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEECTL_2840", 0xc0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_status_2840_print;
+#else
+#define ahc_status_2840_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "STATUS_2840", 0xc1, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_64_btt_print;
+#else
+#define ahc_scb_64_btt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_64_BTT", 0xd0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_cchaddr_print;
+#else
+#define ahc_cchaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCHADDR", 0xe0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_cchcnt_print;
+#else
+#define ahc_cchcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCHCNT", 0xe8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccsgram_print;
+#else
+#define ahc_ccsgram_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSGRAM", 0xe9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccsgaddr_print;
+#else
+#define ahc_ccsgaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSGADDR", 0xea, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccsgctl_print;
+#else
+#define ahc_ccsgctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSGCTL", 0xeb, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbram_print;
+#else
+#define ahc_ccscbram_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBRAM", 0xec, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbaddr_print;
+#else
+#define ahc_ccscbaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBADDR", 0xed, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbctl_print;
+#else
+#define ahc_ccscbctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBCTL", 0xee, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbcnt_print;
+#else
+#define ahc_ccscbcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBCNT", 0xef, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scbbaddr_print;
+#else
+#define ahc_scbbaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCBBADDR", 0xf0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbptr_print;
+#else
+#define ahc_ccscbptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBPTR", 0xf1, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hnscb_qoff_print;
+#else
+#define ahc_hnscb_qoff_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HNSCB_QOFF", 0xf4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_snscb_qoff_print;
+#else
+#define ahc_snscb_qoff_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SNSCB_QOFF", 0xf6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sdscb_qoff_print;
+#else
+#define ahc_sdscb_qoff_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SDSCB_QOFF", 0xf8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qoff_ctlsta_print;
+#else
+#define ahc_qoff_ctlsta_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QOFF_CTLSTA", 0xfa, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dff_thrsh_print;
+#else
+#define ahc_dff_thrsh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFF_THRSH", 0xfb, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sg_cache_shadow_print;
+#else
+#define ahc_sg_cache_shadow_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SG_CACHE_SHADOW", 0xfc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sg_cache_pre_print;
+#else
+#define ahc_sg_cache_pre_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SG_CACHE_PRE", 0xfc, regvalue, cur_col, wrap)
+#endif
+
#define SCSISEQ 0x00
#define TEMODE 0x80
@@ -20,9 +1084,9 @@
#define CLRCHN 0x02
#define SXFRCTL1 0x02
+#define STIMESEL 0x18
#define BITBUCKET 0x80
#define SWRAPEN 0x40
-#define STIMESEL 0x18
#define ENSTIMER 0x04
#define ACTNEGEN 0x02
#define STPWEN 0x01
@@ -47,12 +1111,12 @@
#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 WIDEXFER 0x80
+#define ENABLE_CRC 0x40
+#define SINGLE_EDGE 0x10
#define SCSIID 0x05
#define SCSIOFFSET 0x05
@@ -65,13 +1129,13 @@
#define STCNT 0x08
#define OPTIONMODE 0x08
+#define OPTIONMODE_DEFAULTS 0x03
#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
@@ -116,9 +1180,9 @@
#define REQINIT 0x01
#define SSTAT2 0x0d
+#define SFCNT 0x1f
#define OVERRUN 0x80
#define SHVALID 0x40
-#define SFCNT 0x1f
#define EXP_ACTIVE 0x10
#define CRCVALERR 0x08
#define CRCENDERR 0x04
@@ -156,6 +1220,11 @@
#define SCSIBUSH 0x13
+#define SXFRCTL2 0x13
+#define ASYNC_SETUP 0x07
+#define AUTORSTDIS 0x10
+#define CMDDMAEN 0x08
+
#define SHADDR 0x14
#define SELTIMER 0x18
@@ -172,11 +1241,11 @@
#define ONEBIT 0x08
#define SCAMCTL 0x1a
+#define SCAMLVL 0x03
#define ENSCAMSELO 0x80
#define CLRSCAMSELID 0x40
#define ALTSTIM 0x20
#define DFLTTID 0x10
-#define SCAMLVL 0x03
#define TARGID 0x1b
@@ -184,7 +1253,7 @@
#define SOFT1 0x80
#define SOFT0 0x40
#define SOFTCMDEN 0x20
-#define HAS_BRDCTL 0x10
+#define EXT_BRDCTL 0x10
#define SEEPROM 0x08
#define EEPROM 0x04
#define ROM 0x02
@@ -228,8 +1297,6 @@
#define BUSY_TARGETS 0x20
#define TARG_SCSIRATE 0x20
-#define SRAM_BASE 0x20
-
#define ULTRA_ENB 0x30
#define CMDSIZE_TABLE 0x30
@@ -238,6 +1305,7 @@
#define CMDSIZE_TABLE_TAIL 0x34
#define MWI_RESIDUAL 0x38
+#define TARG_IMMEDIATE_SCB 0x38
#define NEXT_QUEUED_SCB 0x39
@@ -256,7 +1324,8 @@
#define FIFORESET 0x01
#define SEQ_FLAGS 0x3c
-#define IDENTIFY_SEEN 0x80
+#define NOT_IDENTIFIED 0x80
+#define NO_CDB_SENT 0x40
#define TARGET_CMD_IS_TAGGED 0x40
#define DPHASE 0x20
#define TARG_CMD_PENDING 0x10
@@ -275,12 +1344,12 @@
#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 CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
#define WAITING_SCBH 0x40
@@ -329,22 +1398,24 @@
#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 HWSCSIID 0x0f
+#define HSCSIID 0x07
#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 EDGE_TRIG 0x80
#define HOSTCONF 0x5d
@@ -396,6 +1467,8 @@
#define TARG_OFFSET 0x70
+#define SRAM_BASE 0x70
+
#define BCTL 0x84
#define ACE 0x08
#define ENABLE 0x01
@@ -462,13 +1535,13 @@
#define PDATA_REINIT 0x51
#define IGN_WIDE_RES 0x41
#define NO_MATCH 0x31
-#define NO_IDENT 0x21
+#define PROTO_VIOLATION 0x21
#define SEND_REJECT 0x11
#define INT_PEND 0x0f
+#define BAD_PHASE 0x01
#define BRKADRINT 0x08
#define SCSIINT 0x04
#define CMDCMPLT 0x02
-#define BAD_PHASE 0x01
#define SEQINT 0x01
#define CLRINT 0x92
@@ -507,8 +1580,8 @@
#define DFDAT 0x99
#define SCBCNT 0x9a
-#define SCBAUTO 0x80
#define SCBCNT_MASK 0x1f
+#define SCBAUTO 0x80
#define QINFIFO 0x9b
@@ -527,11 +1600,11 @@
#define QOUTCNT 0x9e
#define SCSIPHASE 0x9e
+#define DATA_PHASE_MASK 0x03
#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
@@ -557,8 +1630,8 @@
#define SCB_DATAPTR 0xac
#define SCB_DATACNT 0xb0
-#define SG_LAST_SEG 0x80
#define SG_HIGH_ADDR_BITS 0x7f
+#define SG_LAST_SEG 0x80
#define SCB_SGPTR 0xb4
#define SG_RESID_VALID 0x04
@@ -566,19 +1639,20 @@
#define SG_LIST_NULL 0x01
#define SCB_CONTROL 0xb8
+#define SCB_TAG_TYPE 0x03
+#define STATUS_RCVD 0x80
#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 TWIN_CHNLB 0x80
#define SCB_LUN 0xba
#define LID 0xff
@@ -601,9 +1675,9 @@
#define DO_2840 0x01
#define STATUS_2840 0xc1
-#define EEPROM_TF 0x80
#define BIOS_SEL 0x60
#define ADSEL 0x1e
+#define EEPROM_TF 0x80
#define DI_2840 0x01
#define SCB_64_BTT 0xd0
@@ -647,11 +1721,11 @@
#define SDSCB_QOFF 0xf8
#define QOFF_CTLSTA 0xfa
+#define SCB_QSIZE 0x07
+#define SCB_QSIZE_256 0x06
#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
@@ -702,6 +1776,7 @@
#define SEQ_MAILBOX_SHIFT 0x00
#define TARGET_DATA_IN 0x01
#define HOST_MSG 0xff
+#define MAX_OFFSET 0xff
#define BUS_16_BIT 0x01
#define SCB_UPLOAD_SIZE 0x20
@@ -714,3 +1789,7 @@
#define SG_PREFETCH_CNT 0x04
#define CACHESIZE_MASK 0x02
#define QINFIFO_OFFSET 0x01
+#define DOWNLOAD_CONST_COUNT 0x07
+
+
+/* Exported Labels */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped b/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped
new file mode 100644
index 000000000000..0f6df05e0315
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped
@@ -0,0 +1,1689 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#52 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#36 $
+ */
+
+#include "aic7xxx_osm.h"
+
+static ahc_reg_parse_entry_t SCSISEQ_parse_table[] = {
+ { "SCSIRSTO", 0x01, 0x01 },
+ { "ENAUTOATNP", 0x02, 0x02 },
+ { "ENAUTOATNI", 0x04, 0x04 },
+ { "ENAUTOATNO", 0x08, 0x08 },
+ { "ENRSELI", 0x10, 0x10 },
+ { "ENSELI", 0x20, 0x20 },
+ { "ENSELO", 0x40, 0x40 },
+ { "TEMODE", 0x80, 0x80 }
+};
+
+int
+ahc_scsiseq_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSISEQ_parse_table, 8, "SCSISEQ",
+ 0x00, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SXFRCTL0_parse_table[] = {
+ { "CLRCHN", 0x02, 0x02 },
+ { "SCAMEN", 0x04, 0x04 },
+ { "SPIOEN", 0x08, 0x08 },
+ { "CLRSTCNT", 0x10, 0x10 },
+ { "FAST20", 0x20, 0x20 },
+ { "DFPEXP", 0x40, 0x40 },
+ { "DFON", 0x80, 0x80 }
+};
+
+int
+ahc_sxfrctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SXFRCTL0_parse_table, 7, "SXFRCTL0",
+ 0x01, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SXFRCTL1_parse_table[] = {
+ { "STPWEN", 0x01, 0x01 },
+ { "ACTNEGEN", 0x02, 0x02 },
+ { "ENSTIMER", 0x04, 0x04 },
+ { "ENSPCHK", 0x20, 0x20 },
+ { "SWRAPEN", 0x40, 0x40 },
+ { "BITBUCKET", 0x80, 0x80 },
+ { "STIMESEL", 0x18, 0x18 }
+};
+
+int
+ahc_sxfrctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SXFRCTL1_parse_table, 7, "SXFRCTL1",
+ 0x02, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSISIGO_parse_table[] = {
+ { "ACKO", 0x01, 0x01 },
+ { "REQO", 0x02, 0x02 },
+ { "BSYO", 0x04, 0x04 },
+ { "SELO", 0x08, 0x08 },
+ { "ATNO", 0x10, 0x10 },
+ { "MSGO", 0x20, 0x20 },
+ { "IOO", 0x40, 0x40 },
+ { "CDO", 0x80, 0x80 },
+ { "P_DATAOUT", 0x00, 0x00 },
+ { "P_DATAIN", 0x40, 0x40 },
+ { "P_COMMAND", 0x80, 0x80 },
+ { "P_MESGOUT", 0xa0, 0xa0 },
+ { "P_STATUS", 0xc0, 0xc0 },
+ { "PHASE_MASK", 0xe0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 }
+};
+
+int
+ahc_scsisigo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSISIGO_parse_table, 15, "SCSISIGO",
+ 0x03, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSISIGI_parse_table[] = {
+ { "ACKI", 0x01, 0x01 },
+ { "REQI", 0x02, 0x02 },
+ { "BSYI", 0x04, 0x04 },
+ { "SELI", 0x08, 0x08 },
+ { "ATNI", 0x10, 0x10 },
+ { "MSGI", 0x20, 0x20 },
+ { "IOI", 0x40, 0x40 },
+ { "CDI", 0x80, 0x80 },
+ { "P_DATAOUT", 0x00, 0x00 },
+ { "P_DATAOUT_DT", 0x20, 0x20 },
+ { "P_DATAIN", 0x40, 0x40 },
+ { "P_DATAIN_DT", 0x60, 0x60 },
+ { "P_COMMAND", 0x80, 0x80 },
+ { "P_MESGOUT", 0xa0, 0xa0 },
+ { "P_STATUS", 0xc0, 0xc0 },
+ { "PHASE_MASK", 0xe0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 }
+};
+
+int
+ahc_scsisigi_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSISIGI_parse_table, 17, "SCSISIGI",
+ 0x03, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSIRATE_parse_table[] = {
+ { "SINGLE_EDGE", 0x10, 0x10 },
+ { "ENABLE_CRC", 0x40, 0x40 },
+ { "WIDEXFER", 0x80, 0x80 },
+ { "SXFR_ULTRA2", 0x0f, 0x0f },
+ { "SOFS", 0x0f, 0x0f },
+ { "SXFR", 0x70, 0x70 }
+};
+
+int
+ahc_scsirate_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSIRATE_parse_table, 6, "SCSIRATE",
+ 0x04, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSIID_parse_table[] = {
+ { "TWIN_CHNLB", 0x80, 0x80 },
+ { "OID", 0x0f, 0x0f },
+ { "TWIN_TID", 0x70, 0x70 },
+ { "SOFS_ULTRA2", 0x7f, 0x7f },
+ { "TID", 0xf0, 0xf0 }
+};
+
+int
+ahc_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSIID_parse_table, 5, "SCSIID",
+ 0x05, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scsidatl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCSIDATL",
+ 0x06, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scsidath_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCSIDATH",
+ 0x07, regvalue, cur_col, wrap));
+}
+
+int
+ahc_stcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "STCNT",
+ 0x08, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t OPTIONMODE_parse_table[] = {
+ { "DIS_MSGIN_DUALEDGE", 0x01, 0x01 },
+ { "AUTO_MSGOUT_DE", 0x02, 0x02 },
+ { "SCSIDATL_IMGEN", 0x04, 0x04 },
+ { "EXPPHASEDIS", 0x08, 0x08 },
+ { "BUSFREEREV", 0x10, 0x10 },
+ { "ATNMGMNTEN", 0x20, 0x20 },
+ { "AUTOACKEN", 0x40, 0x40 },
+ { "AUTORATEEN", 0x80, 0x80 },
+ { "OPTIONMODE_DEFAULTS",0x03, 0x03 }
+};
+
+int
+ahc_optionmode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(OPTIONMODE_parse_table, 9, "OPTIONMODE",
+ 0x08, regvalue, cur_col, wrap));
+}
+
+int
+ahc_targcrccnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "TARGCRCCNT",
+ 0x0a, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CLRSINT0_parse_table[] = {
+ { "CLRSPIORDY", 0x02, 0x02 },
+ { "CLRSWRAP", 0x08, 0x08 },
+ { "CLRIOERR", 0x08, 0x08 },
+ { "CLRSELINGO", 0x10, 0x10 },
+ { "CLRSELDI", 0x20, 0x20 },
+ { "CLRSELDO", 0x40, 0x40 }
+};
+
+int
+ahc_clrsint0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CLRSINT0_parse_table, 6, "CLRSINT0",
+ 0x0b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SSTAT0_parse_table[] = {
+ { "DMADONE", 0x01, 0x01 },
+ { "SPIORDY", 0x02, 0x02 },
+ { "SDONE", 0x04, 0x04 },
+ { "SWRAP", 0x08, 0x08 },
+ { "IOERR", 0x08, 0x08 },
+ { "SELINGO", 0x10, 0x10 },
+ { "SELDI", 0x20, 0x20 },
+ { "SELDO", 0x40, 0x40 },
+ { "TARGET", 0x80, 0x80 }
+};
+
+int
+ahc_sstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SSTAT0_parse_table, 9, "SSTAT0",
+ 0x0b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CLRSINT1_parse_table[] = {
+ { "CLRREQINIT", 0x01, 0x01 },
+ { "CLRPHASECHG", 0x02, 0x02 },
+ { "CLRSCSIPERR", 0x04, 0x04 },
+ { "CLRBUSFREE", 0x08, 0x08 },
+ { "CLRSCSIRSTI", 0x20, 0x20 },
+ { "CLRATNO", 0x40, 0x40 },
+ { "CLRSELTIMEO", 0x80, 0x80 }
+};
+
+int
+ahc_clrsint1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CLRSINT1_parse_table, 7, "CLRSINT1",
+ 0x0c, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SSTAT1_parse_table[] = {
+ { "REQINIT", 0x01, 0x01 },
+ { "PHASECHG", 0x02, 0x02 },
+ { "SCSIPERR", 0x04, 0x04 },
+ { "BUSFREE", 0x08, 0x08 },
+ { "PHASEMIS", 0x10, 0x10 },
+ { "SCSIRSTI", 0x20, 0x20 },
+ { "ATNTARG", 0x40, 0x40 },
+ { "SELTO", 0x80, 0x80 }
+};
+
+int
+ahc_sstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SSTAT1_parse_table, 8, "SSTAT1",
+ 0x0c, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SSTAT2_parse_table[] = {
+ { "DUAL_EDGE_ERR", 0x01, 0x01 },
+ { "CRCREQERR", 0x02, 0x02 },
+ { "CRCENDERR", 0x04, 0x04 },
+ { "CRCVALERR", 0x08, 0x08 },
+ { "EXP_ACTIVE", 0x10, 0x10 },
+ { "SHVALID", 0x40, 0x40 },
+ { "OVERRUN", 0x80, 0x80 },
+ { "SFCNT", 0x1f, 0x1f }
+};
+
+int
+ahc_sstat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SSTAT2_parse_table, 8, "SSTAT2",
+ 0x0d, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SSTAT3_parse_table[] = {
+ { "OFFCNT", 0x0f, 0x0f },
+ { "U2OFFCNT", 0x7f, 0x7f },
+ { "SCSICNT", 0xf0, 0xf0 }
+};
+
+int
+ahc_sstat3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SSTAT3_parse_table, 3, "SSTAT3",
+ 0x0e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSIID_ULTRA2_parse_table[] = {
+ { "OID", 0x0f, 0x0f },
+ { "TID", 0xf0, 0xf0 }
+};
+
+int
+ahc_scsiid_ultra2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSIID_ULTRA2_parse_table, 2, "SCSIID_ULTRA2",
+ 0x0f, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SIMODE0_parse_table[] = {
+ { "ENDMADONE", 0x01, 0x01 },
+ { "ENSPIORDY", 0x02, 0x02 },
+ { "ENSDONE", 0x04, 0x04 },
+ { "ENSWRAP", 0x08, 0x08 },
+ { "ENIOERR", 0x08, 0x08 },
+ { "ENSELINGO", 0x10, 0x10 },
+ { "ENSELDI", 0x20, 0x20 },
+ { "ENSELDO", 0x40, 0x40 }
+};
+
+int
+ahc_simode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SIMODE0_parse_table, 8, "SIMODE0",
+ 0x10, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SIMODE1_parse_table[] = {
+ { "ENREQINIT", 0x01, 0x01 },
+ { "ENPHASECHG", 0x02, 0x02 },
+ { "ENSCSIPERR", 0x04, 0x04 },
+ { "ENBUSFREE", 0x08, 0x08 },
+ { "ENPHASEMIS", 0x10, 0x10 },
+ { "ENSCSIRST", 0x20, 0x20 },
+ { "ENATNTARG", 0x40, 0x40 },
+ { "ENSELTIMO", 0x80, 0x80 }
+};
+
+int
+ahc_simode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SIMODE1_parse_table, 8, "SIMODE1",
+ 0x11, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scsibusl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCSIBUSL",
+ 0x12, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scsibush_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCSIBUSH",
+ 0x13, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SXFRCTL2_parse_table[] = {
+ { "CMDDMAEN", 0x08, 0x08 },
+ { "AUTORSTDIS", 0x10, 0x10 },
+ { "ASYNC_SETUP", 0x07, 0x07 }
+};
+
+int
+ahc_sxfrctl2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SXFRCTL2_parse_table, 3, "SXFRCTL2",
+ 0x13, regvalue, cur_col, wrap));
+}
+
+int
+ahc_shaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SHADDR",
+ 0x14, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SELTIMER_parse_table[] = {
+ { "STAGE1", 0x01, 0x01 },
+ { "STAGE2", 0x02, 0x02 },
+ { "STAGE3", 0x04, 0x04 },
+ { "STAGE4", 0x08, 0x08 },
+ { "STAGE5", 0x10, 0x10 },
+ { "STAGE6", 0x20, 0x20 }
+};
+
+int
+ahc_seltimer_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SELTIMER_parse_table, 6, "SELTIMER",
+ 0x18, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SELID_parse_table[] = {
+ { "ONEBIT", 0x08, 0x08 },
+ { "SELID_MASK", 0xf0, 0xf0 }
+};
+
+int
+ahc_selid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SELID_parse_table, 2, "SELID",
+ 0x19, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCAMCTL_parse_table[] = {
+ { "DFLTTID", 0x10, 0x10 },
+ { "ALTSTIM", 0x20, 0x20 },
+ { "CLRSCAMSELID", 0x40, 0x40 },
+ { "ENSCAMSELO", 0x80, 0x80 },
+ { "SCAMLVL", 0x03, 0x03 }
+};
+
+int
+ahc_scamctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCAMCTL_parse_table, 5, "SCAMCTL",
+ 0x1a, regvalue, cur_col, wrap));
+}
+
+int
+ahc_targid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "TARGID",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SPIOCAP_parse_table[] = {
+ { "SSPIOCPS", 0x01, 0x01 },
+ { "ROM", 0x02, 0x02 },
+ { "EEPROM", 0x04, 0x04 },
+ { "SEEPROM", 0x08, 0x08 },
+ { "EXT_BRDCTL", 0x10, 0x10 },
+ { "SOFTCMDEN", 0x20, 0x20 },
+ { "SOFT0", 0x40, 0x40 },
+ { "SOFT1", 0x80, 0x80 }
+};
+
+int
+ahc_spiocap_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SPIOCAP_parse_table, 8, "SPIOCAP",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t BRDCTL_parse_table[] = {
+ { "BRDCTL0", 0x01, 0x01 },
+ { "BRDSTB_ULTRA2", 0x01, 0x01 },
+ { "BRDCTL1", 0x02, 0x02 },
+ { "BRDRW_ULTRA2", 0x02, 0x02 },
+ { "BRDRW", 0x04, 0x04 },
+ { "BRDDAT2", 0x04, 0x04 },
+ { "BRDCS", 0x08, 0x08 },
+ { "BRDDAT3", 0x08, 0x08 },
+ { "BRDSTB", 0x10, 0x10 },
+ { "BRDDAT4", 0x10, 0x10 },
+ { "BRDDAT5", 0x20, 0x20 },
+ { "BRDDAT6", 0x40, 0x40 },
+ { "BRDDAT7", 0x80, 0x80 }
+};
+
+int
+ahc_brdctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(BRDCTL_parse_table, 13, "BRDCTL",
+ 0x1d, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEECTL_parse_table[] = {
+ { "SEEDI", 0x01, 0x01 },
+ { "SEEDO", 0x02, 0x02 },
+ { "SEECK", 0x04, 0x04 },
+ { "SEECS", 0x08, 0x08 },
+ { "SEERDY", 0x10, 0x10 },
+ { "SEEMS", 0x20, 0x20 },
+ { "EXTARBREQ", 0x40, 0x40 },
+ { "EXTARBACK", 0x80, 0x80 }
+};
+
+int
+ahc_seectl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEECTL_parse_table, 8, "SEECTL",
+ 0x1e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SBLKCTL_parse_table[] = {
+ { "XCVR", 0x01, 0x01 },
+ { "SELWIDE", 0x02, 0x02 },
+ { "ENAB20", 0x04, 0x04 },
+ { "SELBUSB", 0x08, 0x08 },
+ { "ENAB40", 0x08, 0x08 },
+ { "AUTOFLUSHDIS", 0x20, 0x20 },
+ { "DIAGLEDON", 0x40, 0x40 },
+ { "DIAGLEDEN", 0x80, 0x80 }
+};
+
+int
+ahc_sblkctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SBLKCTL_parse_table, 8, "SBLKCTL",
+ 0x1f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_busy_targets_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "BUSY_TARGETS",
+ 0x20, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ultra_enb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ULTRA_ENB",
+ 0x30, regvalue, cur_col, wrap));
+}
+
+int
+ahc_disc_dsb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DISC_DSB",
+ 0x32, regvalue, cur_col, wrap));
+}
+
+int
+ahc_cmdsize_table_tail_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CMDSIZE_TABLE_TAIL",
+ 0x34, regvalue, cur_col, wrap));
+}
+
+int
+ahc_mwi_residual_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "MWI_RESIDUAL",
+ 0x38, regvalue, cur_col, wrap));
+}
+
+int
+ahc_next_queued_scb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "NEXT_QUEUED_SCB",
+ 0x39, regvalue, cur_col, wrap));
+}
+
+int
+ahc_msg_out_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "MSG_OUT",
+ 0x3a, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DMAPARAMS_parse_table[] = {
+ { "FIFORESET", 0x01, 0x01 },
+ { "FIFOFLUSH", 0x02, 0x02 },
+ { "DIRECTION", 0x04, 0x04 },
+ { "HDMAEN", 0x08, 0x08 },
+ { "HDMAENACK", 0x08, 0x08 },
+ { "SDMAEN", 0x10, 0x10 },
+ { "SDMAENACK", 0x10, 0x10 },
+ { "SCSIEN", 0x20, 0x20 },
+ { "WIDEODD", 0x40, 0x40 },
+ { "PRELOADEN", 0x80, 0x80 }
+};
+
+int
+ahc_dmaparams_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DMAPARAMS_parse_table, 10, "DMAPARAMS",
+ 0x3b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEQ_FLAGS_parse_table[] = {
+ { "NO_DISCONNECT", 0x01, 0x01 },
+ { "SPHASE_PENDING", 0x02, 0x02 },
+ { "DPHASE_PENDING", 0x04, 0x04 },
+ { "CMDPHASE_PENDING", 0x08, 0x08 },
+ { "TARG_CMD_PENDING", 0x10, 0x10 },
+ { "DPHASE", 0x20, 0x20 },
+ { "NO_CDB_SENT", 0x40, 0x40 },
+ { "TARGET_CMD_IS_TAGGED",0x40, 0x40 },
+ { "NOT_IDENTIFIED", 0x80, 0x80 }
+};
+
+int
+ahc_seq_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEQ_FLAGS_parse_table, 9, "SEQ_FLAGS",
+ 0x3c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_saved_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SAVED_SCSIID",
+ 0x3d, regvalue, cur_col, wrap));
+}
+
+int
+ahc_saved_lun_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SAVED_LUN",
+ 0x3e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t LASTPHASE_parse_table[] = {
+ { "MSGI", 0x20, 0x20 },
+ { "IOI", 0x40, 0x40 },
+ { "CDI", 0x80, 0x80 },
+ { "P_DATAOUT", 0x00, 0x00 },
+ { "P_BUSFREE", 0x01, 0x01 },
+ { "P_DATAIN", 0x40, 0x40 },
+ { "P_COMMAND", 0x80, 0x80 },
+ { "P_MESGOUT", 0xa0, 0xa0 },
+ { "P_STATUS", 0xc0, 0xc0 },
+ { "PHASE_MASK", 0xe0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 }
+};
+
+int
+ahc_lastphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(LASTPHASE_parse_table, 11, "LASTPHASE",
+ 0x3f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_waiting_scbh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "WAITING_SCBH",
+ 0x40, regvalue, cur_col, wrap));
+}
+
+int
+ahc_disconnected_scbh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DISCONNECTED_SCBH",
+ 0x41, regvalue, cur_col, wrap));
+}
+
+int
+ahc_free_scbh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "FREE_SCBH",
+ 0x42, regvalue, cur_col, wrap));
+}
+
+int
+ahc_complete_scbh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "COMPLETE_SCBH",
+ 0x43, regvalue, cur_col, wrap));
+}
+
+int
+ahc_hscb_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HSCB_ADDR",
+ 0x44, regvalue, cur_col, wrap));
+}
+
+int
+ahc_shared_data_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SHARED_DATA_ADDR",
+ 0x48, regvalue, cur_col, wrap));
+}
+
+int
+ahc_kernel_qinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "KERNEL_QINPOS",
+ 0x4c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QINPOS",
+ 0x4d, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qoutpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QOUTPOS",
+ 0x4e, regvalue, cur_col, wrap));
+}
+
+int
+ahc_kernel_tqinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "KERNEL_TQINPOS",
+ 0x4f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_tqinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "TQINPOS",
+ 0x50, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t ARG_1_parse_table[] = {
+ { "CONT_TARG_SESSION", 0x02, 0x02 },
+ { "CONT_MSG_LOOP", 0x04, 0x04 },
+ { "EXIT_MSG_LOOP", 0x08, 0x08 },
+ { "MSGOUT_PHASEMIS", 0x10, 0x10 },
+ { "SEND_REJ", 0x20, 0x20 },
+ { "SEND_SENSE", 0x40, 0x40 },
+ { "SEND_MSG", 0x80, 0x80 }
+};
+
+int
+ahc_arg_1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(ARG_1_parse_table, 7, "ARG_1",
+ 0x51, regvalue, cur_col, wrap));
+}
+
+int
+ahc_arg_2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ARG_2",
+ 0x52, regvalue, cur_col, wrap));
+}
+
+int
+ahc_last_msg_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "LAST_MSG",
+ 0x53, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSISEQ_TEMPLATE_parse_table[] = {
+ { "ENAUTOATNP", 0x02, 0x02 },
+ { "ENAUTOATNI", 0x04, 0x04 },
+ { "ENAUTOATNO", 0x08, 0x08 },
+ { "ENRSELI", 0x10, 0x10 },
+ { "ENSELI", 0x20, 0x20 },
+ { "ENSELO", 0x40, 0x40 }
+};
+
+int
+ahc_scsiseq_template_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSISEQ_TEMPLATE_parse_table, 6, "SCSISEQ_TEMPLATE",
+ 0x54, regvalue, cur_col, wrap));
+}
+
+int
+ahc_data_count_odd_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DATA_COUNT_ODD",
+ 0x55, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t HA_274_BIOSGLOBAL_parse_table[] = {
+ { "HA_274_EXTENDED_TRANS",0x01, 0x01 }
+};
+
+int
+ahc_ha_274_biosglobal_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(HA_274_BIOSGLOBAL_parse_table, 1, "HA_274_BIOSGLOBAL",
+ 0x56, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEQ_FLAGS2_parse_table[] = {
+ { "SCB_DMA", 0x01, 0x01 },
+ { "TARGET_MSG_PENDING", 0x02, 0x02 }
+};
+
+int
+ahc_seq_flags2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEQ_FLAGS2_parse_table, 2, "SEQ_FLAGS2",
+ 0x57, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSICONF_parse_table[] = {
+ { "ENSPCHK", 0x20, 0x20 },
+ { "RESET_SCSI", 0x40, 0x40 },
+ { "TERM_ENB", 0x80, 0x80 },
+ { "HSCSIID", 0x07, 0x07 },
+ { "HWSCSIID", 0x0f, 0x0f }
+};
+
+int
+ahc_scsiconf_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSICONF_parse_table, 5, "SCSICONF",
+ 0x5a, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t INTDEF_parse_table[] = {
+ { "EDGE_TRIG", 0x80, 0x80 },
+ { "VECTOR", 0x0f, 0x0f }
+};
+
+int
+ahc_intdef_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(INTDEF_parse_table, 2, "INTDEF",
+ 0x5c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_hostconf_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HOSTCONF",
+ 0x5d, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t HA_274_BIOSCTRL_parse_table[] = {
+ { "CHANNEL_B_PRIMARY", 0x08, 0x08 },
+ { "BIOSMODE", 0x30, 0x30 },
+ { "BIOSDISABLED", 0x30, 0x30 }
+};
+
+int
+ahc_ha_274_biosctrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(HA_274_BIOSCTRL_parse_table, 3, "HA_274_BIOSCTRL",
+ 0x5f, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEQCTL_parse_table[] = {
+ { "LOADRAM", 0x01, 0x01 },
+ { "SEQRESET", 0x02, 0x02 },
+ { "STEP", 0x04, 0x04 },
+ { "BRKADRINTEN", 0x08, 0x08 },
+ { "FASTMODE", 0x10, 0x10 },
+ { "FAILDIS", 0x20, 0x20 },
+ { "PAUSEDIS", 0x40, 0x40 },
+ { "PERRORDIS", 0x80, 0x80 }
+};
+
+int
+ahc_seqctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEQCTL_parse_table, 8, "SEQCTL",
+ 0x60, regvalue, cur_col, wrap));
+}
+
+int
+ahc_seqram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SEQRAM",
+ 0x61, regvalue, cur_col, wrap));
+}
+
+int
+ahc_seqaddr0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SEQADDR0",
+ 0x62, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEQADDR1_parse_table[] = {
+ { "SEQADDR1_MASK", 0x01, 0x01 }
+};
+
+int
+ahc_seqaddr1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEQADDR1_parse_table, 1, "SEQADDR1",
+ 0x63, regvalue, cur_col, wrap));
+}
+
+int
+ahc_accum_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ACCUM",
+ 0x64, regvalue, cur_col, wrap));
+}
+
+int
+ahc_sindex_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SINDEX",
+ 0x65, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dindex_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DINDEX",
+ 0x66, regvalue, cur_col, wrap));
+}
+
+int
+ahc_allones_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ALLONES",
+ 0x69, regvalue, cur_col, wrap));
+}
+
+int
+ahc_allzeros_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ALLZEROS",
+ 0x6a, regvalue, cur_col, wrap));
+}
+
+int
+ahc_none_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "NONE",
+ 0x6a, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t FLAGS_parse_table[] = {
+ { "CARRY", 0x01, 0x01 },
+ { "ZERO", 0x02, 0x02 }
+};
+
+int
+ahc_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(FLAGS_parse_table, 2, "FLAGS",
+ 0x6b, regvalue, cur_col, wrap));
+}
+
+int
+ahc_sindir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SINDIR",
+ 0x6c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dindir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DINDIR",
+ 0x6d, regvalue, cur_col, wrap));
+}
+
+int
+ahc_function1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "FUNCTION1",
+ 0x6e, regvalue, cur_col, wrap));
+}
+
+int
+ahc_stack_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "STACK",
+ 0x6f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_targ_offset_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "TARG_OFFSET",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+int
+ahc_sram_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SRAM_BASE",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t BCTL_parse_table[] = {
+ { "ENABLE", 0x01, 0x01 },
+ { "ACE", 0x08, 0x08 }
+};
+
+int
+ahc_bctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(BCTL_parse_table, 2, "BCTL",
+ 0x84, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DSCOMMAND0_parse_table[] = {
+ { "CIOPARCKEN", 0x01, 0x01 },
+ { "USCBSIZE32", 0x02, 0x02 },
+ { "RAMPS", 0x04, 0x04 },
+ { "INTSCBRAMSEL", 0x08, 0x08 },
+ { "EXTREQLCK", 0x10, 0x10 },
+ { "MPARCKEN", 0x20, 0x20 },
+ { "DPARCKEN", 0x40, 0x40 },
+ { "CACHETHEN", 0x80, 0x80 }
+};
+
+int
+ahc_dscommand0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DSCOMMAND0_parse_table, 8, "DSCOMMAND0",
+ 0x84, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t BUSTIME_parse_table[] = {
+ { "BON", 0x0f, 0x0f },
+ { "BOFF", 0xf0, 0xf0 }
+};
+
+int
+ahc_bustime_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(BUSTIME_parse_table, 2, "BUSTIME",
+ 0x85, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DSCOMMAND1_parse_table[] = {
+ { "HADDLDSEL0", 0x01, 0x01 },
+ { "HADDLDSEL1", 0x02, 0x02 },
+ { "DSLATT", 0xfc, 0xfc }
+};
+
+int
+ahc_dscommand1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DSCOMMAND1_parse_table, 3, "DSCOMMAND1",
+ 0x85, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t BUSSPD_parse_table[] = {
+ { "STBON", 0x07, 0x07 },
+ { "STBOFF", 0x38, 0x38 },
+ { "DFTHRSH_75", 0x80, 0x80 },
+ { "DFTHRSH", 0xc0, 0xc0 },
+ { "DFTHRSH_100", 0xc0, 0xc0 }
+};
+
+int
+ahc_busspd_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(BUSSPD_parse_table, 5, "BUSSPD",
+ 0x86, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t HS_MAILBOX_parse_table[] = {
+ { "SEQ_MAILBOX", 0x0f, 0x0f },
+ { "HOST_TQINPOS", 0x80, 0x80 },
+ { "HOST_MAILBOX", 0xf0, 0xf0 }
+};
+
+int
+ahc_hs_mailbox_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(HS_MAILBOX_parse_table, 3, "HS_MAILBOX",
+ 0x86, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DSPCISTATUS_parse_table[] = {
+ { "DFTHRSH_100", 0xc0, 0xc0 }
+};
+
+int
+ahc_dspcistatus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DSPCISTATUS_parse_table, 1, "DSPCISTATUS",
+ 0x86, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t HCNTRL_parse_table[] = {
+ { "CHIPRST", 0x01, 0x01 },
+ { "CHIPRSTACK", 0x01, 0x01 },
+ { "INTEN", 0x02, 0x02 },
+ { "PAUSE", 0x04, 0x04 },
+ { "IRQMS", 0x08, 0x08 },
+ { "SWINT", 0x10, 0x10 },
+ { "POWRDN", 0x40, 0x40 }
+};
+
+int
+ahc_hcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(HCNTRL_parse_table, 7, "HCNTRL",
+ 0x87, regvalue, cur_col, wrap));
+}
+
+int
+ahc_haddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HADDR",
+ 0x88, regvalue, cur_col, wrap));
+}
+
+int
+ahc_hcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HCNT",
+ 0x8c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scbptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCBPTR",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t INTSTAT_parse_table[] = {
+ { "SEQINT", 0x01, 0x01 },
+ { "CMDCMPLT", 0x02, 0x02 },
+ { "SCSIINT", 0x04, 0x04 },
+ { "BRKADRINT", 0x08, 0x08 },
+ { "BAD_PHASE", 0x01, 0x01 },
+ { "INT_PEND", 0x0f, 0x0f },
+ { "SEND_REJECT", 0x11, 0x11 },
+ { "PROTO_VIOLATION", 0x21, 0x21 },
+ { "NO_MATCH", 0x31, 0x31 },
+ { "IGN_WIDE_RES", 0x41, 0x41 },
+ { "PDATA_REINIT", 0x51, 0x51 },
+ { "HOST_MSG_LOOP", 0x61, 0x61 },
+ { "BAD_STATUS", 0x71, 0x71 },
+ { "PERR_DETECTED", 0x81, 0x81 },
+ { "DATA_OVERRUN", 0x91, 0x91 },
+ { "MKMSG_FAILED", 0xa1, 0xa1 },
+ { "MISSED_BUSFREE", 0xb1, 0xb1 },
+ { "SCB_MISMATCH", 0xc1, 0xc1 },
+ { "NO_FREE_SCB", 0xd1, 0xd1 },
+ { "OUT_OF_RANGE", 0xe1, 0xe1 },
+ { "SEQINT_MASK", 0xf1, 0xf1 }
+};
+
+int
+ahc_intstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(INTSTAT_parse_table, 21, "INTSTAT",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CLRINT_parse_table[] = {
+ { "CLRSEQINT", 0x01, 0x01 },
+ { "CLRCMDINT", 0x02, 0x02 },
+ { "CLRSCSIINT", 0x04, 0x04 },
+ { "CLRBRKADRINT", 0x08, 0x08 },
+ { "CLRPARERR", 0x10, 0x10 }
+};
+
+int
+ahc_clrint_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CLRINT_parse_table, 5, "CLRINT",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t ERROR_parse_table[] = {
+ { "ILLHADDR", 0x01, 0x01 },
+ { "ILLSADDR", 0x02, 0x02 },
+ { "ILLOPCODE", 0x04, 0x04 },
+ { "SQPARERR", 0x08, 0x08 },
+ { "DPARERR", 0x10, 0x10 },
+ { "MPARERR", 0x20, 0x20 },
+ { "PCIERRSTAT", 0x40, 0x40 },
+ { "CIOPARERR", 0x80, 0x80 }
+};
+
+int
+ahc_error_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(ERROR_parse_table, 8, "ERROR",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DFCNTRL_parse_table[] = {
+ { "FIFORESET", 0x01, 0x01 },
+ { "FIFOFLUSH", 0x02, 0x02 },
+ { "DIRECTION", 0x04, 0x04 },
+ { "HDMAEN", 0x08, 0x08 },
+ { "HDMAENACK", 0x08, 0x08 },
+ { "SDMAEN", 0x10, 0x10 },
+ { "SDMAENACK", 0x10, 0x10 },
+ { "SCSIEN", 0x20, 0x20 },
+ { "WIDEODD", 0x40, 0x40 },
+ { "PRELOADEN", 0x80, 0x80 }
+};
+
+int
+ahc_dfcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DFCNTRL_parse_table, 10, "DFCNTRL",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DFSTATUS_parse_table[] = {
+ { "FIFOEMP", 0x01, 0x01 },
+ { "FIFOFULL", 0x02, 0x02 },
+ { "DFTHRESH", 0x04, 0x04 },
+ { "HDONE", 0x08, 0x08 },
+ { "MREQPEND", 0x10, 0x10 },
+ { "FIFOQWDEMP", 0x20, 0x20 },
+ { "DFCACHETH", 0x40, 0x40 },
+ { "PRELOAD_AVAIL", 0x80, 0x80 }
+};
+
+int
+ahc_dfstatus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DFSTATUS_parse_table, 8, "DFSTATUS",
+ 0x94, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dfwaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DFWADDR",
+ 0x95, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dfraddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DFRADDR",
+ 0x97, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dfdat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DFDAT",
+ 0x99, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCBCNT_parse_table[] = {
+ { "SCBAUTO", 0x80, 0x80 },
+ { "SCBCNT_MASK", 0x1f, 0x1f }
+};
+
+int
+ahc_scbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCBCNT_parse_table, 2, "SCBCNT",
+ 0x9a, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qinfifo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QINFIFO",
+ 0x9b, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qincnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QINCNT",
+ 0x9c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qoutfifo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QOUTFIFO",
+ 0x9d, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CRCCONTROL1_parse_table[] = {
+ { "TARGCRCCNTEN", 0x04, 0x04 },
+ { "TARGCRCENDEN", 0x08, 0x08 },
+ { "CRCREQCHKEN", 0x10, 0x10 },
+ { "CRCENDCHKEN", 0x20, 0x20 },
+ { "CRCVALCHKEN", 0x40, 0x40 },
+ { "CRCONSEEN", 0x80, 0x80 }
+};
+
+int
+ahc_crccontrol1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CRCCONTROL1_parse_table, 6, "CRCCONTROL1",
+ 0x9d, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qoutcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QOUTCNT",
+ 0x9e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSIPHASE_parse_table[] = {
+ { "DATA_OUT_PHASE", 0x01, 0x01 },
+ { "DATA_IN_PHASE", 0x02, 0x02 },
+ { "MSG_OUT_PHASE", 0x04, 0x04 },
+ { "MSG_IN_PHASE", 0x08, 0x08 },
+ { "COMMAND_PHASE", 0x10, 0x10 },
+ { "STATUS_PHASE", 0x20, 0x20 },
+ { "DATA_PHASE_MASK", 0x03, 0x03 }
+};
+
+int
+ahc_scsiphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSIPHASE_parse_table, 7, "SCSIPHASE",
+ 0x9e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SFUNCT_parse_table[] = {
+ { "ALT_MODE", 0x80, 0x80 }
+};
+
+int
+ahc_sfunct_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SFUNCT_parse_table, 1, "SFUNCT",
+ 0x9f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_BASE",
+ 0xa0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_cdb_ptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_CDB_PTR",
+ 0xa0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_residual_sgptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_RESIDUAL_SGPTR",
+ 0xa4, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_scsi_status_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_SCSI_STATUS",
+ 0xa8, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_target_phases_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_TARGET_PHASES",
+ 0xa9, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_target_data_dir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_TARGET_DATA_DIR",
+ 0xaa, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_target_itag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_TARGET_ITAG",
+ 0xab, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_dataptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_DATAPTR",
+ 0xac, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_DATACNT_parse_table[] = {
+ { "SG_LAST_SEG", 0x80, 0x80 },
+ { "SG_HIGH_ADDR_BITS", 0x7f, 0x7f }
+};
+
+int
+ahc_scb_datacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_DATACNT_parse_table, 2, "SCB_DATACNT",
+ 0xb0, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_SGPTR_parse_table[] = {
+ { "SG_LIST_NULL", 0x01, 0x01 },
+ { "SG_FULL_RESID", 0x02, 0x02 },
+ { "SG_RESID_VALID", 0x04, 0x04 }
+};
+
+int
+ahc_scb_sgptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_SGPTR_parse_table, 3, "SCB_SGPTR",
+ 0xb4, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_CONTROL_parse_table[] = {
+ { "DISCONNECTED", 0x04, 0x04 },
+ { "ULTRAENB", 0x08, 0x08 },
+ { "MK_MESSAGE", 0x10, 0x10 },
+ { "TAG_ENB", 0x20, 0x20 },
+ { "DISCENB", 0x40, 0x40 },
+ { "TARGET_SCB", 0x80, 0x80 },
+ { "STATUS_RCVD", 0x80, 0x80 },
+ { "SCB_TAG_TYPE", 0x03, 0x03 }
+};
+
+int
+ahc_scb_control_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_CONTROL_parse_table, 8, "SCB_CONTROL",
+ 0xb8, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_SCSIID_parse_table[] = {
+ { "TWIN_CHNLB", 0x80, 0x80 },
+ { "OID", 0x0f, 0x0f },
+ { "TWIN_TID", 0x70, 0x70 },
+ { "TID", 0xf0, 0xf0 }
+};
+
+int
+ahc_scb_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_SCSIID_parse_table, 4, "SCB_SCSIID",
+ 0xb9, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_LUN_parse_table[] = {
+ { "LID", 0xff, 0xff }
+};
+
+int
+ahc_scb_lun_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_LUN_parse_table, 1, "SCB_LUN",
+ 0xba, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_tag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_TAG",
+ 0xbb, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_cdb_len_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_CDB_LEN",
+ 0xbc, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_scsirate_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_SCSIRATE",
+ 0xbd, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_scsioffset_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_SCSIOFFSET",
+ 0xbe, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_next_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_NEXT",
+ 0xbf, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_64_spare_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_64_SPARE",
+ 0xc0, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEECTL_2840_parse_table[] = {
+ { "DO_2840", 0x01, 0x01 },
+ { "CK_2840", 0x02, 0x02 },
+ { "CS_2840", 0x04, 0x04 }
+};
+
+int
+ahc_seectl_2840_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEECTL_2840_parse_table, 3, "SEECTL_2840",
+ 0xc0, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t STATUS_2840_parse_table[] = {
+ { "DI_2840", 0x01, 0x01 },
+ { "EEPROM_TF", 0x80, 0x80 },
+ { "ADSEL", 0x1e, 0x1e },
+ { "BIOS_SEL", 0x60, 0x60 }
+};
+
+int
+ahc_status_2840_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(STATUS_2840_parse_table, 4, "STATUS_2840",
+ 0xc1, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_64_btt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_64_BTT",
+ 0xd0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_cchaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCHADDR",
+ 0xe0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_cchcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCHCNT",
+ 0xe8, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccsgram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSGRAM",
+ 0xe9, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccsgaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSGADDR",
+ 0xea, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CCSGCTL_parse_table[] = {
+ { "CCSGRESET", 0x01, 0x01 },
+ { "SG_FETCH_NEEDED", 0x02, 0x02 },
+ { "CCSGEN", 0x08, 0x08 },
+ { "CCSGDONE", 0x80, 0x80 }
+};
+
+int
+ahc_ccsgctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CCSGCTL_parse_table, 4, "CCSGCTL",
+ 0xeb, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccscbram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSCBRAM",
+ 0xec, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccscbaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSCBADDR",
+ 0xed, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CCSCBCTL_parse_table[] = {
+ { "CCSCBRESET", 0x01, 0x01 },
+ { "CCSCBDIR", 0x04, 0x04 },
+ { "CCSCBEN", 0x08, 0x08 },
+ { "CCARREN", 0x10, 0x10 },
+ { "ARRDONE", 0x40, 0x40 },
+ { "CCSCBDONE", 0x80, 0x80 }
+};
+
+int
+ahc_ccscbctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CCSCBCTL_parse_table, 6, "CCSCBCTL",
+ 0xee, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccscbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSCBCNT",
+ 0xef, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scbbaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCBBADDR",
+ 0xf0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccscbptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSCBPTR",
+ 0xf1, regvalue, cur_col, wrap));
+}
+
+int
+ahc_hnscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HNSCB_QOFF",
+ 0xf4, regvalue, cur_col, wrap));
+}
+
+int
+ahc_snscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SNSCB_QOFF",
+ 0xf6, regvalue, cur_col, wrap));
+}
+
+int
+ahc_sdscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SDSCB_QOFF",
+ 0xf8, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t QOFF_CTLSTA_parse_table[] = {
+ { "SDSCB_ROLLOVER", 0x10, 0x10 },
+ { "SNSCB_ROLLOVER", 0x20, 0x20 },
+ { "SCB_AVAIL", 0x40, 0x40 },
+ { "SCB_QSIZE_256", 0x06, 0x06 },
+ { "SCB_QSIZE", 0x07, 0x07 }
+};
+
+int
+ahc_qoff_ctlsta_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(QOFF_CTLSTA_parse_table, 5, "QOFF_CTLSTA",
+ 0xfa, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DFF_THRSH_parse_table[] = {
+ { "RD_DFTHRSH_MIN", 0x00, 0x00 },
+ { "WR_DFTHRSH_MIN", 0x00, 0x00 },
+ { "RD_DFTHRSH_25", 0x01, 0x01 },
+ { "RD_DFTHRSH_50", 0x02, 0x02 },
+ { "RD_DFTHRSH_63", 0x03, 0x03 },
+ { "RD_DFTHRSH_75", 0x04, 0x04 },
+ { "RD_DFTHRSH_85", 0x05, 0x05 },
+ { "RD_DFTHRSH_90", 0x06, 0x06 },
+ { "RD_DFTHRSH", 0x07, 0x07 },
+ { "RD_DFTHRSH_MAX", 0x07, 0x07 },
+ { "WR_DFTHRSH_25", 0x10, 0x10 },
+ { "WR_DFTHRSH_50", 0x20, 0x20 },
+ { "WR_DFTHRSH_63", 0x30, 0x30 },
+ { "WR_DFTHRSH_75", 0x40, 0x40 },
+ { "WR_DFTHRSH_85", 0x50, 0x50 },
+ { "WR_DFTHRSH_90", 0x60, 0x60 },
+ { "WR_DFTHRSH", 0x70, 0x70 },
+ { "WR_DFTHRSH_MAX", 0x70, 0x70 }
+};
+
+int
+ahc_dff_thrsh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DFF_THRSH_parse_table, 18, "DFF_THRSH",
+ 0xfb, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SG_CACHE_SHADOW_parse_table[] = {
+ { "LAST_SEG_DONE", 0x01, 0x01 },
+ { "LAST_SEG", 0x02, 0x02 },
+ { "ODD_SEG", 0x04, 0x04 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahc_sg_cache_shadow_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SG_CACHE_SHADOW_parse_table, 4, "SG_CACHE_SHADOW",
+ 0xfc, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SG_CACHE_PRE_parse_table[] = {
+ { "LAST_SEG_DONE", 0x01, 0x01 },
+ { "LAST_SEG", 0x02, 0x02 },
+ { "ODD_SEG", 0x04, 0x04 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahc_sg_cache_pre_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SG_CACHE_PRE_parse_table, 4, "SG_CACHE_PRE",
+ 0xfc, regvalue, cur_col, wrap));
+}
+
diff --git a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
index 3504b548eb82..4f97b79f97c1 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
+++ b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
@@ -2,13 +2,13 @@
* DO NOT EDIT - This file is automatically generated
* from the following source files:
*
- * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#37 $
- * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#24 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#52 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#36 $
*/
static uint8_t seqprog[] = {
0xb2, 0x00, 0x00, 0x08,
0xf7, 0x11, 0x22, 0x08,
- 0x00, 0x65, 0xe0, 0x59,
+ 0x00, 0x65, 0xea, 0x59,
0xf7, 0x01, 0x02, 0x08,
0xff, 0x6a, 0x24, 0x08,
0x40, 0x00, 0x40, 0x68,
@@ -21,15 +21,15 @@ static uint8_t seqprog[] = {
0x01, 0x4d, 0xc8, 0x30,
0x00, 0x4c, 0x12, 0x70,
0x01, 0x39, 0xa2, 0x30,
- 0x00, 0x6a, 0xb2, 0x5e,
+ 0x00, 0x6a, 0xd2, 0x5e,
0x01, 0x51, 0x20, 0x31,
0x01, 0x57, 0xae, 0x00,
0x0d, 0x6a, 0x76, 0x00,
- 0x00, 0x51, 0x04, 0x5e,
+ 0x00, 0x51, 0x24, 0x5e,
0x01, 0x51, 0xc8, 0x30,
0x00, 0x39, 0xc8, 0x60,
0x00, 0xbb, 0x30, 0x70,
- 0xc1, 0x6a, 0xca, 0x5e,
+ 0xc1, 0x6a, 0xea, 0x5e,
0x01, 0xbf, 0x72, 0x30,
0x01, 0x40, 0x7e, 0x31,
0x01, 0x90, 0x80, 0x30,
@@ -42,17 +42,17 @@ static uint8_t seqprog[] = {
0x08, 0x6a, 0x18, 0x00,
0x08, 0x11, 0x22, 0x00,
0x60, 0x0b, 0x00, 0x78,
- 0x40, 0x0b, 0xfc, 0x68,
+ 0x40, 0x0b, 0xfa, 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, 0xee, 0x5d,
+ 0x48, 0x6a, 0x0e, 0x5e,
0x01, 0x6a, 0xdc, 0x01,
0x88, 0x6a, 0xcc, 0x00,
- 0x48, 0x6a, 0xee, 0x5d,
+ 0x48, 0x6a, 0x0e, 0x5e,
0x01, 0x6a, 0x26, 0x01,
0xf0, 0x19, 0x7a, 0x08,
0x0f, 0x18, 0xc8, 0x08,
@@ -63,8 +63,8 @@ static uint8_t seqprog[] = {
0x80, 0x3d, 0x7a, 0x00,
0x01, 0x3d, 0xd8, 0x31,
0x01, 0x3d, 0x32, 0x31,
- 0x10, 0x03, 0x46, 0x79,
- 0x00, 0x65, 0xf4, 0x58,
+ 0x10, 0x03, 0x4c, 0x79,
+ 0x00, 0x65, 0xf2, 0x58,
0x80, 0x66, 0xae, 0x78,
0x01, 0x66, 0xd8, 0x31,
0x01, 0x66, 0x32, 0x31,
@@ -72,29 +72,29 @@ static uint8_t seqprog[] = {
0x40, 0x66, 0x82, 0x68,
0x01, 0x3c, 0x78, 0x00,
0x10, 0x03, 0x9e, 0x78,
- 0x00, 0x65, 0xf4, 0x58,
+ 0x00, 0x65, 0xf2, 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, 0x46, 0x79,
- 0x00, 0x65, 0xf4, 0x58,
+ 0x10, 0x03, 0x4c, 0x79,
+ 0x00, 0x65, 0xf2, 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, 0x3a, 0x69,
- 0x10, 0x03, 0x38, 0x69,
- 0x00, 0x65, 0x1e, 0x41,
+ 0x10, 0x6a, 0x78, 0x00,
+ 0x02, 0x57, 0x40, 0x69,
+ 0x10, 0x03, 0x3e, 0x69,
+ 0x00, 0x65, 0x20, 0x41,
0x02, 0x57, 0xae, 0x00,
0x00, 0x65, 0x9e, 0x40,
- 0x61, 0x6a, 0xca, 0x5e,
- 0x08, 0x51, 0x1e, 0x71,
+ 0x61, 0x6a, 0xea, 0x5e,
+ 0x08, 0x51, 0x20, 0x71,
0x02, 0x0b, 0xb2, 0x78,
0x00, 0x65, 0xae, 0x40,
0x1a, 0x01, 0x02, 0x00,
@@ -105,8 +105,8 @@ static uint8_t seqprog[] = {
0x08, 0x1f, 0xc4, 0x78,
0x80, 0x3d, 0x7a, 0x00,
0x20, 0x6a, 0x16, 0x00,
- 0x00, 0x65, 0xc0, 0x41,
- 0x00, 0x65, 0xa4, 0x5e,
+ 0x00, 0x65, 0xca, 0x41,
+ 0x00, 0x65, 0xc4, 0x5e,
0x00, 0x65, 0x12, 0x40,
0x20, 0x11, 0xd2, 0x68,
0x20, 0x6a, 0x18, 0x00,
@@ -123,168 +123,173 @@ static uint8_t seqprog[] = {
0x80, 0x65, 0xca, 0x00,
0x01, 0x65, 0x00, 0x34,
0x01, 0x54, 0x00, 0x34,
- 0x1a, 0x01, 0x02, 0x00,
- 0x08, 0xb8, 0xf0, 0x78,
+ 0x08, 0xb8, 0xee, 0x78,
0x20, 0x01, 0x02, 0x00,
0x02, 0xbd, 0x08, 0x34,
0x01, 0xbd, 0x08, 0x34,
0x08, 0x01, 0x02, 0x00,
- 0x02, 0x0b, 0xf6, 0x78,
+ 0x02, 0x0b, 0xf4, 0x78,
0xf7, 0x01, 0x02, 0x08,
0x01, 0x06, 0xcc, 0x34,
0xb2, 0x00, 0x00, 0x08,
- 0x40, 0x6a, 0x16, 0x00,
0x01, 0x40, 0x20, 0x31,
0x01, 0xbf, 0x80, 0x30,
0x01, 0xb9, 0x7a, 0x30,
0x01, 0xba, 0x7c, 0x30,
0x00, 0x65, 0xea, 0x58,
- 0x80, 0x0b, 0xbc, 0x79,
- 0xe4, 0x6a, 0x60, 0x5d,
- 0x80, 0xba, 0x76, 0x5d,
- 0x20, 0xb8, 0x16, 0x79,
- 0x20, 0x6a, 0x76, 0x5d,
- 0x00, 0xab, 0x76, 0x5d,
+ 0x80, 0x0b, 0xc2, 0x79,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x01, 0xab, 0xac, 0x30,
+ 0xe4, 0x6a, 0x80, 0x5d,
+ 0x40, 0x6a, 0x16, 0x00,
+ 0x80, 0xba, 0x96, 0x5d,
+ 0x20, 0xb8, 0x18, 0x79,
+ 0x20, 0x6a, 0x96, 0x5d,
+ 0x00, 0xab, 0x96, 0x5d,
0x01, 0xa9, 0x78, 0x30,
- 0x10, 0xb8, 0x1e, 0x79,
- 0xe4, 0x6a, 0x60, 0x5d,
+ 0x10, 0xb8, 0x20, 0x79,
+ 0xe4, 0x6a, 0x80, 0x5d,
0x00, 0x65, 0xae, 0x40,
- 0x10, 0x03, 0x36, 0x69,
- 0x08, 0x3c, 0x52, 0x69,
- 0x04, 0x3c, 0x8a, 0x69,
- 0x02, 0x3c, 0x90, 0x69,
- 0x01, 0x3c, 0x3c, 0x79,
- 0x01, 0x6a, 0xa2, 0x30,
- 0x00, 0x65, 0x9c, 0x59,
- 0x04, 0x51, 0x2c, 0x61,
- 0x00, 0x6a, 0xb2, 0x5e,
+ 0x10, 0x03, 0x3c, 0x69,
+ 0x08, 0x3c, 0x58, 0x69,
+ 0x04, 0x3c, 0x90, 0x69,
+ 0x02, 0x3c, 0x96, 0x69,
+ 0x01, 0x3c, 0x42, 0x79,
+ 0xff, 0x6a, 0x70, 0x00,
+ 0x00, 0x65, 0xa2, 0x59,
+ 0x00, 0x6a, 0xd2, 0x5e,
+ 0xff, 0x38, 0x30, 0x71,
0x0d, 0x6a, 0x76, 0x00,
- 0x00, 0xbb, 0x04, 0x5e,
- 0x00, 0x65, 0x16, 0x41,
+ 0x00, 0x38, 0x24, 0x5e,
+ 0x00, 0x65, 0xea, 0x58,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x00, 0x65, 0x18, 0x41,
0xa4, 0x6a, 0x06, 0x00,
- 0x00, 0x65, 0xf4, 0x58,
+ 0x00, 0x65, 0xf2, 0x58,
0x00, 0x65, 0xae, 0x40,
- 0xe4, 0x6a, 0x60, 0x5d,
- 0x20, 0x3c, 0x42, 0x79,
- 0x02, 0x6a, 0x76, 0x5d,
- 0x04, 0x6a, 0x76, 0x5d,
- 0x01, 0x03, 0x44, 0x69,
+ 0xe4, 0x6a, 0x80, 0x5d,
+ 0x20, 0x3c, 0x48, 0x79,
+ 0x02, 0x6a, 0x96, 0x5d,
+ 0x04, 0x6a, 0x96, 0x5d,
+ 0x01, 0x03, 0x4a, 0x69,
0xf7, 0x11, 0x22, 0x08,
0xff, 0x6a, 0x24, 0x08,
0xff, 0x6a, 0x06, 0x08,
0x01, 0x6a, 0x7e, 0x00,
- 0x00, 0x65, 0x9c, 0x59,
+ 0x00, 0x65, 0xa2, 0x59,
0x00, 0x65, 0x04, 0x40,
0x80, 0x86, 0xc8, 0x08,
0x01, 0x4f, 0xc8, 0x30,
- 0x00, 0x50, 0x64, 0x61,
- 0xc4, 0x6a, 0x60, 0x5d,
- 0x40, 0x3c, 0x60, 0x79,
- 0x28, 0x6a, 0x76, 0x5d,
- 0x00, 0x65, 0x44, 0x41,
- 0x08, 0x6a, 0x76, 0x5d,
- 0x00, 0x65, 0x44, 0x41,
- 0x84, 0x6a, 0x60, 0x5d,
- 0x00, 0x65, 0xf4, 0x58,
+ 0x00, 0x50, 0x6a, 0x61,
+ 0xc4, 0x6a, 0x80, 0x5d,
+ 0x40, 0x3c, 0x66, 0x79,
+ 0x28, 0x6a, 0x96, 0x5d,
+ 0x00, 0x65, 0x4a, 0x41,
+ 0x08, 0x6a, 0x96, 0x5d,
+ 0x00, 0x65, 0x4a, 0x41,
+ 0x84, 0x6a, 0x80, 0x5d,
+ 0x00, 0x65, 0xf2, 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, 0x86, 0x79,
+ 0xff, 0x64, 0x8c, 0x79,
0x08, 0x01, 0x02, 0x00,
- 0x02, 0x0b, 0x78, 0x79,
- 0x01, 0x64, 0x7e, 0x61,
+ 0x02, 0x0b, 0x7e, 0x79,
+ 0x01, 0x64, 0x84, 0x61,
0xf7, 0x01, 0x02, 0x08,
0x01, 0x06, 0xd8, 0x31,
0x01, 0x06, 0x32, 0x31,
0xff, 0x64, 0xc8, 0x18,
- 0xff, 0x64, 0x78, 0x69,
+ 0xff, 0x64, 0x7e, 0x69,
0xf7, 0x3c, 0x78, 0x08,
- 0x00, 0x65, 0x1e, 0x41,
+ 0x00, 0x65, 0x20, 0x41,
0x40, 0xaa, 0x7e, 0x10,
- 0x04, 0xaa, 0x60, 0x5d,
- 0x00, 0x65, 0x52, 0x42,
- 0xc4, 0x6a, 0x60, 0x5d,
+ 0x04, 0xaa, 0x80, 0x5d,
+ 0x00, 0x65, 0x5c, 0x42,
+ 0xc4, 0x6a, 0x80, 0x5d,
0xc0, 0x6a, 0x7e, 0x00,
- 0x00, 0xa8, 0x76, 0x5d,
+ 0x00, 0xa8, 0x96, 0x5d,
0xe4, 0x6a, 0x06, 0x00,
- 0x00, 0x6a, 0x76, 0x5d,
- 0x00, 0x65, 0x44, 0x41,
- 0x10, 0x3c, 0xa0, 0x69,
- 0x00, 0xbb, 0x80, 0x44,
+ 0x00, 0x6a, 0x96, 0x5d,
+ 0x00, 0x65, 0x4a, 0x41,
+ 0x10, 0x3c, 0xa6, 0x69,
+ 0x00, 0xbb, 0x9c, 0x44,
0x18, 0x6a, 0xda, 0x01,
0x01, 0x69, 0xd8, 0x31,
0x1c, 0x6a, 0xd0, 0x01,
0x09, 0xee, 0xdc, 0x01,
- 0x80, 0xee, 0xa8, 0x79,
+ 0x80, 0xee, 0xae, 0x79,
0xff, 0x6a, 0xdc, 0x09,
0x01, 0x93, 0x26, 0x01,
0x03, 0x6a, 0x2a, 0x01,
0x01, 0x69, 0x32, 0x31,
- 0x1c, 0x6a, 0xd2, 0x5d,
+ 0x1c, 0x6a, 0xf2, 0x5d,
0x0a, 0x93, 0x26, 0x01,
- 0x00, 0x65, 0x9a, 0x5e,
+ 0x00, 0x65, 0xba, 0x5e,
0x01, 0x50, 0xa0, 0x18,
0x02, 0x6a, 0x22, 0x05,
+ 0x1a, 0x01, 0x02, 0x00,
0x80, 0x6a, 0x74, 0x00,
- 0x80, 0x3c, 0x78, 0x00,
- 0x00, 0x65, 0xca, 0x5d,
+ 0x40, 0x6a, 0x78, 0x00,
+ 0x40, 0x6a, 0x16, 0x00,
+ 0x00, 0x65, 0xea, 0x5d,
0x01, 0x3f, 0xc8, 0x30,
- 0xbf, 0x64, 0x52, 0x7a,
- 0x80, 0x64, 0xa6, 0x73,
- 0xa0, 0x64, 0x04, 0x74,
- 0xc0, 0x64, 0xf8, 0x73,
- 0xe0, 0x64, 0x34, 0x74,
- 0x01, 0x6a, 0xca, 0x5e,
- 0x00, 0x65, 0xc0, 0x41,
+ 0xbf, 0x64, 0x5c, 0x7a,
+ 0x80, 0x64, 0xb0, 0x73,
+ 0xa0, 0x64, 0x12, 0x74,
+ 0xc0, 0x64, 0x06, 0x74,
+ 0xe0, 0x64, 0x42, 0x74,
+ 0x01, 0x6a, 0xea, 0x5e,
+ 0x00, 0x65, 0xca, 0x41,
0xf7, 0x11, 0x22, 0x08,
0x01, 0x06, 0xd4, 0x30,
0xff, 0x6a, 0x24, 0x08,
0xf7, 0x01, 0x02, 0x08,
- 0x09, 0x0c, 0xda, 0x79,
+ 0x09, 0x0c, 0xe4, 0x79,
0x08, 0x0c, 0x04, 0x68,
- 0xb1, 0x6a, 0xca, 0x5e,
+ 0xb1, 0x6a, 0xea, 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,
+ 0xc0, 0x6a, 0x78, 0x04,
0xff, 0x6a, 0xc8, 0x08,
0x08, 0xa4, 0x48, 0x19,
0x00, 0xa5, 0x4a, 0x21,
0x00, 0xa6, 0x4c, 0x21,
0x00, 0xa7, 0x4e, 0x25,
- 0x08, 0xeb, 0xce, 0x7e,
- 0x80, 0xeb, 0xfa, 0x79,
+ 0x08, 0xeb, 0xee, 0x7e,
+ 0x80, 0xeb, 0x04, 0x7a,
0xff, 0x6a, 0xd6, 0x09,
- 0x08, 0xeb, 0xfe, 0x69,
+ 0x08, 0xeb, 0x08, 0x6a,
0xff, 0x6a, 0xd4, 0x0c,
- 0x80, 0xa3, 0xce, 0x6e,
- 0x88, 0xeb, 0x14, 0x72,
- 0x08, 0xeb, 0xce, 0x6e,
- 0x04, 0xea, 0x18, 0xe2,
- 0x08, 0xee, 0xce, 0x6e,
+ 0x80, 0xa3, 0xee, 0x6e,
+ 0x88, 0xeb, 0x1e, 0x72,
+ 0x08, 0xeb, 0xee, 0x6e,
+ 0x04, 0xea, 0x22, 0xe2,
+ 0x08, 0xee, 0xee, 0x6e,
0x04, 0x6a, 0xd0, 0x81,
0x05, 0xa4, 0xc0, 0x89,
0x03, 0xa5, 0xc2, 0x31,
0x09, 0x6a, 0xd6, 0x05,
- 0x00, 0x65, 0xfc, 0x59,
+ 0x00, 0x65, 0x06, 0x5a,
0x06, 0xa4, 0xd4, 0x89,
- 0x80, 0x94, 0xce, 0x7e,
+ 0x80, 0x94, 0xee, 0x7e,
0x07, 0xe9, 0x10, 0x31,
- 0x01, 0x8c, 0x20, 0x7a,
+ 0x01, 0x8c, 0x2a, 0x7a,
0x01, 0x55, 0xaa, 0x10,
0x01, 0xe9, 0x46, 0x31,
- 0x00, 0xa3, 0xac, 0x5e,
- 0x00, 0x65, 0xee, 0x59,
+ 0x00, 0xa3, 0xcc, 0x5e,
+ 0x00, 0x65, 0xf8, 0x59,
0x01, 0xa4, 0xca, 0x30,
- 0x01, 0x55, 0x2c, 0x7a,
+ 0x01, 0x55, 0x36, 0x7a,
0x04, 0x65, 0xca, 0x00,
- 0x80, 0xa3, 0x30, 0x7a,
+ 0x80, 0xa3, 0x3a, 0x7a,
0x02, 0x65, 0xca, 0x00,
0x01, 0x65, 0xf8, 0x31,
0x80, 0x93, 0x26, 0x01,
@@ -292,168 +297,168 @@ static uint8_t seqprog[] = {
0x01, 0x8c, 0xc8, 0x30,
0x00, 0x88, 0xc8, 0x18,
0x02, 0x64, 0xc8, 0x88,
- 0xff, 0x64, 0xce, 0x7e,
- 0xff, 0x8d, 0x46, 0x6a,
- 0xff, 0x8e, 0x46, 0x6a,
+ 0xff, 0x64, 0xee, 0x7e,
+ 0xff, 0x8d, 0x50, 0x6a,
+ 0xff, 0x8e, 0x50, 0x6a,
0x03, 0x8c, 0xd4, 0x98,
- 0x00, 0x65, 0xce, 0x56,
+ 0x00, 0x65, 0xee, 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, 0x56, 0x6a,
- 0x21, 0x6a, 0xca, 0x46,
+ 0xc0, 0x3c, 0x60, 0x7a,
+ 0x21, 0x6a, 0xea, 0x5e,
0xa8, 0x6a, 0x76, 0x00,
0x79, 0x6a, 0x76, 0x00,
- 0x40, 0x3f, 0x5e, 0x6a,
+ 0x40, 0x3f, 0x68, 0x6a,
0x04, 0x3b, 0x76, 0x00,
0x04, 0x6a, 0xd4, 0x81,
- 0x20, 0x3c, 0x66, 0x7a,
- 0x51, 0x6a, 0xca, 0x5e,
- 0x00, 0x65, 0x80, 0x42,
+ 0x20, 0x3c, 0x70, 0x7a,
+ 0x51, 0x6a, 0xea, 0x5e,
+ 0x00, 0x65, 0x8a, 0x42,
0x20, 0x3c, 0x78, 0x00,
- 0x00, 0xb3, 0xac, 0x5e,
+ 0x00, 0xb3, 0xcc, 0x5e,
0x07, 0xac, 0x10, 0x31,
0x05, 0xb3, 0x46, 0x31,
0x88, 0x6a, 0xcc, 0x00,
- 0xac, 0x6a, 0xe0, 0x5d,
+ 0xac, 0x6a, 0x00, 0x5e,
0xa3, 0x6a, 0xcc, 0x00,
- 0xb3, 0x6a, 0xe4, 0x5d,
- 0x00, 0x65, 0x36, 0x5a,
+ 0xb3, 0x6a, 0x04, 0x5e,
+ 0x00, 0x65, 0x40, 0x5a,
0xfd, 0xa4, 0x48, 0x09,
0x01, 0x8c, 0xaa, 0x08,
0x03, 0x8c, 0x10, 0x30,
- 0x00, 0x65, 0xd8, 0x5d,
- 0x01, 0xa4, 0x92, 0x7a,
+ 0x00, 0x65, 0xf8, 0x5d,
+ 0x01, 0xa4, 0x9c, 0x7a,
0x04, 0x3b, 0x76, 0x08,
0x01, 0x3b, 0x26, 0x31,
0x80, 0x02, 0x04, 0x00,
- 0x10, 0x0c, 0x88, 0x7a,
- 0x03, 0x9e, 0x8a, 0x6a,
+ 0x10, 0x0c, 0x92, 0x7a,
+ 0x03, 0x9e, 0x94, 0x6a,
0x7f, 0x02, 0x04, 0x08,
- 0x91, 0x6a, 0xca, 0x5e,
- 0x00, 0x65, 0xc0, 0x41,
+ 0x91, 0x6a, 0xea, 0x5e,
+ 0x00, 0x65, 0xca, 0x41,
0x01, 0xa4, 0xca, 0x30,
- 0x80, 0xa3, 0x98, 0x7a,
+ 0x80, 0xa3, 0xa2, 0x7a,
0x02, 0x65, 0xca, 0x00,
- 0x01, 0x55, 0x9c, 0x7a,
+ 0x01, 0x55, 0xa6, 0x7a,
0x04, 0x65, 0xca, 0x00,
0x01, 0x65, 0xf8, 0x31,
0x01, 0x3b, 0x26, 0x31,
- 0x00, 0x65, 0x02, 0x5a,
- 0x01, 0xfc, 0xaa, 0x6a,
- 0x80, 0x0b, 0xa0, 0x6a,
- 0x10, 0x0c, 0xa0, 0x7a,
- 0x20, 0x93, 0xa0, 0x6a,
+ 0x00, 0x65, 0x0c, 0x5a,
+ 0x01, 0xfc, 0xb4, 0x6a,
+ 0x80, 0x0b, 0xaa, 0x6a,
+ 0x10, 0x0c, 0xaa, 0x7a,
+ 0x20, 0x93, 0xaa, 0x6a,
0x02, 0x93, 0x26, 0x01,
- 0x02, 0xfc, 0xb4, 0x7a,
- 0x40, 0x0d, 0xce, 0x6a,
+ 0x02, 0xfc, 0xbe, 0x7a,
+ 0x40, 0x0d, 0xd8, 0x6a,
0x01, 0xa4, 0x48, 0x01,
- 0x00, 0x65, 0xce, 0x42,
- 0x40, 0x0d, 0xba, 0x6a,
- 0x00, 0x65, 0x02, 0x5a,
- 0x00, 0x65, 0xac, 0x42,
- 0x80, 0xfc, 0xc4, 0x7a,
- 0x80, 0xa4, 0xc4, 0x6a,
+ 0x00, 0x65, 0xd8, 0x42,
+ 0x40, 0x0d, 0xc4, 0x6a,
+ 0x00, 0x65, 0x0c, 0x5a,
+ 0x00, 0x65, 0xb6, 0x42,
+ 0x80, 0xfc, 0xce, 0x7a,
+ 0x80, 0xa4, 0xce, 0x6a,
0xff, 0xa5, 0x4a, 0x19,
0xff, 0xa6, 0x4c, 0x21,
0xff, 0xa7, 0x4e, 0x21,
0xf8, 0xfc, 0x48, 0x09,
0xff, 0x6a, 0xaa, 0x08,
- 0x04, 0xfc, 0xcc, 0x7a,
+ 0x04, 0xfc, 0xd6, 0x7a,
0x01, 0x55, 0xaa, 0x00,
0xff, 0x6a, 0x46, 0x09,
- 0x04, 0x3b, 0xe6, 0x6a,
+ 0x04, 0x3b, 0xf0, 0x6a,
0x02, 0x93, 0x26, 0x01,
- 0x01, 0x94, 0xd0, 0x7a,
- 0x01, 0x94, 0xd0, 0x7a,
- 0x01, 0x94, 0xd0, 0x7a,
- 0x01, 0x94, 0xd0, 0x7a,
- 0x01, 0x94, 0xd0, 0x7a,
- 0x01, 0xa4, 0xe4, 0x7a,
- 0x01, 0xfc, 0xde, 0x7a,
- 0x01, 0x94, 0xe6, 0x6a,
- 0x00, 0x65, 0x80, 0x42,
- 0x01, 0x94, 0xe4, 0x7a,
- 0x10, 0x94, 0xe6, 0x6a,
+ 0x01, 0x94, 0xda, 0x7a,
+ 0x01, 0x94, 0xda, 0x7a,
+ 0x01, 0x94, 0xda, 0x7a,
+ 0x01, 0x94, 0xda, 0x7a,
+ 0x01, 0x94, 0xda, 0x7a,
+ 0x01, 0xa4, 0xee, 0x7a,
+ 0x01, 0xfc, 0xe8, 0x7a,
+ 0x01, 0x94, 0xf0, 0x6a,
+ 0x00, 0x65, 0x8a, 0x42,
+ 0x01, 0x94, 0xee, 0x7a,
+ 0x10, 0x94, 0xf0, 0x6a,
0xd7, 0x93, 0x26, 0x09,
- 0x28, 0x93, 0xea, 0x6a,
+ 0x28, 0x93, 0xf4, 0x6a,
0x01, 0x85, 0x0a, 0x01,
- 0x02, 0xfc, 0xf2, 0x6a,
+ 0x02, 0xfc, 0xfc, 0x6a,
0x01, 0x14, 0x46, 0x31,
0xff, 0x6a, 0x10, 0x09,
0xfe, 0x85, 0x0a, 0x09,
- 0xff, 0x38, 0x00, 0x6b,
- 0x80, 0xa3, 0x00, 0x7b,
- 0x80, 0x0b, 0xfe, 0x7a,
- 0x04, 0x3b, 0x00, 0x7b,
+ 0xff, 0x38, 0x0a, 0x6b,
+ 0x80, 0xa3, 0x0a, 0x7b,
+ 0x80, 0x0b, 0x08, 0x7b,
+ 0x04, 0x3b, 0x0a, 0x7b,
0xbf, 0x3b, 0x76, 0x08,
0x01, 0x3b, 0x26, 0x31,
- 0x00, 0x65, 0x02, 0x5a,
- 0x01, 0x0b, 0x0e, 0x6b,
- 0x10, 0x0c, 0x02, 0x7b,
- 0x04, 0x93, 0x0c, 0x6b,
- 0x01, 0x94, 0x0a, 0x7b,
- 0x10, 0x94, 0x0c, 0x6b,
+ 0x00, 0x65, 0x0c, 0x5a,
+ 0x01, 0x0b, 0x18, 0x6b,
+ 0x10, 0x0c, 0x0c, 0x7b,
+ 0x04, 0x93, 0x16, 0x6b,
+ 0x01, 0x94, 0x14, 0x7b,
+ 0x10, 0x94, 0x16, 0x6b,
0xc7, 0x93, 0x26, 0x09,
0x01, 0x99, 0xd4, 0x30,
- 0x38, 0x93, 0x10, 0x6b,
- 0xff, 0x08, 0x62, 0x6b,
- 0xff, 0x09, 0x62, 0x6b,
- 0xff, 0x0a, 0x62, 0x6b,
- 0xff, 0x38, 0x2c, 0x7b,
+ 0x38, 0x93, 0x1a, 0x6b,
+ 0xff, 0x08, 0x6c, 0x6b,
+ 0xff, 0x09, 0x6c, 0x6b,
+ 0xff, 0x0a, 0x6c, 0x6b,
+ 0xff, 0x38, 0x36, 0x7b,
0x04, 0x14, 0x10, 0x31,
0x01, 0x38, 0x18, 0x31,
0x02, 0x6a, 0x1a, 0x31,
0x88, 0x6a, 0xcc, 0x00,
- 0x14, 0x6a, 0xe6, 0x5d,
- 0x00, 0x38, 0xd2, 0x5d,
+ 0x14, 0x6a, 0x06, 0x5e,
+ 0x00, 0x38, 0xf2, 0x5d,
0xff, 0x6a, 0x70, 0x08,
- 0x00, 0x65, 0x58, 0x43,
- 0x80, 0xa3, 0x32, 0x7b,
- 0x01, 0xa4, 0x48, 0x01,
0x00, 0x65, 0x62, 0x43,
- 0x08, 0xeb, 0x38, 0x7b,
- 0x00, 0x65, 0x02, 0x5a,
- 0x08, 0xeb, 0x34, 0x6b,
+ 0x80, 0xa3, 0x3c, 0x7b,
+ 0x01, 0xa4, 0x48, 0x01,
+ 0x00, 0x65, 0x6c, 0x43,
+ 0x08, 0xeb, 0x42, 0x7b,
+ 0x00, 0x65, 0x0c, 0x5a,
+ 0x08, 0xeb, 0x3e, 0x6b,
0x07, 0xe9, 0x10, 0x31,
0x01, 0xe9, 0xca, 0x30,
0x01, 0x65, 0x46, 0x31,
- 0x00, 0x6a, 0xac, 0x5e,
+ 0x00, 0x6a, 0xcc, 0x5e,
0x88, 0x6a, 0xcc, 0x00,
- 0xa4, 0x6a, 0xe6, 0x5d,
- 0x08, 0x6a, 0xd2, 0x5d,
+ 0xa4, 0x6a, 0x06, 0x5e,
+ 0x08, 0x6a, 0xf2, 0x5d,
0x0d, 0x93, 0x26, 0x01,
- 0x00, 0x65, 0x9a, 0x5e,
+ 0x00, 0x65, 0xba, 0x5e,
0x88, 0x6a, 0xcc, 0x00,
- 0x00, 0x65, 0x7c, 0x5e,
+ 0x00, 0x65, 0x9c, 0x5e,
0x01, 0x99, 0x46, 0x31,
- 0x00, 0xa3, 0xac, 0x5e,
+ 0x00, 0xa3, 0xcc, 0x5e,
0x01, 0x88, 0x10, 0x31,
- 0x00, 0x65, 0x36, 0x5a,
- 0x00, 0x65, 0xee, 0x59,
+ 0x00, 0x65, 0x40, 0x5a,
+ 0x00, 0x65, 0xf8, 0x59,
0x03, 0x8c, 0x10, 0x30,
- 0x00, 0x65, 0xd8, 0x5d,
- 0x01, 0x8c, 0x60, 0x7b,
+ 0x00, 0x65, 0xf8, 0x5d,
+ 0x01, 0x8c, 0x6a, 0x7b,
0x01, 0x55, 0xaa, 0x10,
- 0x80, 0x0b, 0x80, 0x6a,
- 0x80, 0x0b, 0x6a, 0x6b,
- 0x01, 0x0c, 0x64, 0x7b,
- 0x10, 0x0c, 0x80, 0x7a,
- 0x03, 0x9e, 0x80, 0x6a,
- 0x00, 0x65, 0xf8, 0x59,
- 0x00, 0x6a, 0xac, 0x5e,
- 0x01, 0xa4, 0x8a, 0x6b,
- 0xff, 0x38, 0x80, 0x7b,
+ 0x80, 0x0b, 0x8a, 0x6a,
+ 0x80, 0x0b, 0x74, 0x6b,
+ 0x01, 0x0c, 0x6e, 0x7b,
+ 0x10, 0x0c, 0x8a, 0x7a,
+ 0x03, 0x9e, 0x8a, 0x6a,
+ 0x00, 0x65, 0x02, 0x5a,
+ 0x00, 0x6a, 0xcc, 0x5e,
+ 0x01, 0xa4, 0x94, 0x6b,
+ 0xff, 0x38, 0x8a, 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, 0x82, 0x43,
+ 0x00, 0x65, 0x8c, 0x43,
0x03, 0x08, 0x40, 0x31,
0x03, 0x08, 0x40, 0x31,
0x01, 0x08, 0x40, 0x31,
@@ -462,19 +467,19 @@ static uint8_t seqprog[] = {
0xfd, 0xb4, 0x68, 0x09,
0x12, 0x01, 0x02, 0x00,
0x12, 0x01, 0x02, 0x00,
- 0x04, 0x3c, 0xc0, 0x79,
+ 0x04, 0x3c, 0xca, 0x79,
0xfb, 0x3c, 0x78, 0x08,
- 0x04, 0x93, 0x1e, 0x79,
- 0x01, 0x0c, 0x96, 0x6b,
- 0x01, 0x55, 0x1e, 0x79,
- 0x80, 0x04, 0x1e, 0x79,
- 0xe4, 0x6a, 0x60, 0x5d,
- 0x23, 0x6a, 0x76, 0x5d,
- 0x01, 0x6a, 0x76, 0x5d,
- 0x00, 0x65, 0x1e, 0x41,
- 0x00, 0x65, 0xc0, 0x41,
- 0x80, 0x3c, 0xaa, 0x6b,
- 0x21, 0x6a, 0xca, 0x46,
+ 0x04, 0x93, 0x20, 0x79,
+ 0x01, 0x0c, 0xa0, 0x6b,
+ 0x01, 0x55, 0x20, 0x79,
+ 0x80, 0x04, 0x20, 0x79,
+ 0xe4, 0x6a, 0x80, 0x5d,
+ 0x23, 0x6a, 0x96, 0x5d,
+ 0x01, 0x6a, 0x96, 0x5d,
+ 0x00, 0x65, 0x20, 0x41,
+ 0x00, 0x65, 0xca, 0x41,
+ 0x80, 0x3c, 0xb4, 0x7b,
+ 0x21, 0x6a, 0xea, 0x5e,
0x01, 0xbc, 0x18, 0x31,
0x02, 0x6a, 0x1a, 0x31,
0x02, 0x6a, 0xf8, 0x01,
@@ -484,16 +489,16 @@ static uint8_t seqprog[] = {
0xff, 0x6a, 0x12, 0x08,
0xff, 0x6a, 0x14, 0x08,
0xf3, 0xbc, 0xd4, 0x18,
- 0xa0, 0x6a, 0xd0, 0x53,
+ 0xa0, 0x6a, 0xda, 0x53,
0x04, 0xa0, 0x10, 0x31,
0xac, 0x6a, 0x26, 0x01,
0x04, 0xa0, 0x10, 0x31,
0x03, 0x08, 0x18, 0x31,
0x88, 0x6a, 0xcc, 0x00,
- 0xa0, 0x6a, 0xe6, 0x5d,
- 0x00, 0xbc, 0xd2, 0x5d,
+ 0xa0, 0x6a, 0x06, 0x5e,
+ 0x00, 0xbc, 0xf2, 0x5d,
0x3d, 0x6a, 0x26, 0x01,
- 0x00, 0x65, 0xe8, 0x43,
+ 0x00, 0x65, 0xf2, 0x43,
0xff, 0x6a, 0x10, 0x09,
0xa4, 0x6a, 0x26, 0x01,
0x0c, 0xa0, 0x32, 0x31,
@@ -503,117 +508,128 @@ static uint8_t seqprog[] = {
0x36, 0x6a, 0x26, 0x01,
0x02, 0x93, 0x26, 0x01,
0x35, 0x6a, 0x26, 0x01,
- 0x00, 0x65, 0x8e, 0x5e,
- 0x00, 0x65, 0x8e, 0x5e,
+ 0x00, 0x65, 0xae, 0x5e,
+ 0x00, 0x65, 0xae, 0x5e,
0x02, 0x93, 0x26, 0x01,
- 0x04, 0x0b, 0xec, 0x6b,
- 0x10, 0x0c, 0xe8, 0x7b,
- 0x01, 0x03, 0xec, 0x6b,
- 0x20, 0x93, 0xe8, 0x6b,
+ 0xbf, 0x3c, 0x78, 0x08,
+ 0x04, 0x0b, 0xf8, 0x6b,
+ 0x10, 0x0c, 0xf4, 0x7b,
+ 0x01, 0x03, 0xf8, 0x6b,
+ 0x20, 0x93, 0xfa, 0x6b,
+ 0x04, 0x0b, 0x00, 0x6c,
+ 0x40, 0x3c, 0x78, 0x00,
0xc7, 0x93, 0x26, 0x09,
- 0x38, 0x93, 0xf2, 0x6b,
- 0x10, 0x01, 0x02, 0x00,
- 0x00, 0x65, 0xc0, 0x41,
- 0x80, 0x3c, 0xfc, 0x6b,
- 0x21, 0x6a, 0xca, 0x46,
+ 0x38, 0x93, 0x02, 0x6c,
+ 0x00, 0x65, 0xca, 0x41,
+ 0x80, 0x3c, 0x68, 0x6c,
0x01, 0x06, 0x50, 0x31,
- 0x00, 0x65, 0xc0, 0x41,
+ 0x80, 0xb8, 0x70, 0x01,
+ 0x00, 0x65, 0xca, 0x41,
0x10, 0x3f, 0x06, 0x00,
0x10, 0x6a, 0x06, 0x00,
0x01, 0x3a, 0xca, 0x30,
- 0x80, 0x65, 0x20, 0x64,
- 0x10, 0xb8, 0x44, 0x6c,
+ 0x80, 0x65, 0x2e, 0x64,
+ 0x10, 0xb8, 0x52, 0x6c,
0xc0, 0xba, 0xca, 0x00,
- 0x40, 0xb8, 0x10, 0x6c,
+ 0x40, 0xb8, 0x1e, 0x6c,
0xbf, 0x65, 0xca, 0x08,
- 0x20, 0xb8, 0x24, 0x7c,
+ 0x20, 0xb8, 0x32, 0x7c,
0x01, 0x65, 0x0c, 0x30,
- 0x00, 0x65, 0xca, 0x5d,
- 0xa0, 0x3f, 0x2c, 0x64,
+ 0x00, 0x65, 0xea, 0x5d,
+ 0xa0, 0x3f, 0x3a, 0x64,
0x23, 0xb8, 0x0c, 0x08,
- 0x00, 0x65, 0xca, 0x5d,
- 0xa0, 0x3f, 0x2c, 0x64,
- 0x00, 0xbb, 0x24, 0x44,
- 0xff, 0x65, 0x24, 0x64,
- 0x00, 0x65, 0x44, 0x44,
+ 0x00, 0x65, 0xea, 0x5d,
+ 0xa0, 0x3f, 0x3a, 0x64,
+ 0x00, 0xbb, 0x32, 0x44,
+ 0xff, 0x65, 0x32, 0x64,
+ 0x00, 0x65, 0x52, 0x44,
0x40, 0x6a, 0x18, 0x00,
0x01, 0x65, 0x0c, 0x30,
- 0x00, 0x65, 0xca, 0x5d,
- 0xa0, 0x3f, 0x00, 0x74,
+ 0x00, 0x65, 0xea, 0x5d,
+ 0xa0, 0x3f, 0x0e, 0x74,
0x40, 0x6a, 0x18, 0x00,
0x01, 0x3a, 0xa6, 0x30,
0x08, 0x6a, 0x74, 0x00,
- 0x00, 0x65, 0xc0, 0x41,
- 0x64, 0x6a, 0x5a, 0x5d,
- 0x80, 0x64, 0xca, 0x6c,
- 0x04, 0x64, 0x90, 0x74,
- 0x02, 0x64, 0x9e, 0x74,
- 0x00, 0x6a, 0x60, 0x74,
- 0x03, 0x64, 0xbc, 0x74,
- 0x23, 0x64, 0x4c, 0x74,
- 0x08, 0x64, 0x5c, 0x74,
- 0x61, 0x6a, 0xca, 0x5e,
- 0x00, 0x65, 0xca, 0x5d,
- 0x08, 0x51, 0xc2, 0x71,
- 0x00, 0x65, 0x44, 0x44,
- 0x80, 0x04, 0x5a, 0x7c,
- 0x51, 0x6a, 0x50, 0x5d,
- 0x01, 0x51, 0x5a, 0x64,
- 0x01, 0xa4, 0x56, 0x7c,
- 0x01, 0x55, 0x5c, 0x7c,
- 0x41, 0x6a, 0xca, 0x5e,
- 0x00, 0x65, 0x5c, 0x44,
- 0x07, 0x6a, 0x46, 0x5d,
+ 0x00, 0x65, 0xca, 0x41,
+ 0x64, 0x6a, 0x7a, 0x5d,
+ 0x80, 0x64, 0xea, 0x6c,
+ 0x04, 0x64, 0xac, 0x74,
+ 0x02, 0x64, 0xbc, 0x74,
+ 0x00, 0x6a, 0x72, 0x74,
+ 0x03, 0x64, 0xda, 0x74,
+ 0x23, 0x64, 0x5a, 0x74,
+ 0x08, 0x64, 0x6e, 0x74,
+ 0x61, 0x6a, 0xea, 0x5e,
+ 0x00, 0x65, 0xea, 0x5d,
+ 0x08, 0x51, 0xcc, 0x71,
+ 0x00, 0x65, 0x52, 0x44,
+ 0x80, 0x04, 0x6c, 0x7c,
+ 0x51, 0x6a, 0x70, 0x5d,
+ 0x01, 0x51, 0x6c, 0x64,
+ 0x01, 0xa4, 0x64, 0x7c,
+ 0x01, 0x55, 0x6e, 0x7c,
+ 0x41, 0x6a, 0xea, 0x5e,
+ 0x00, 0x65, 0x6e, 0x44,
+ 0x21, 0x6a, 0xea, 0x5e,
+ 0x00, 0x65, 0x6e, 0x44,
+ 0x07, 0x6a, 0x66, 0x5d,
0x01, 0x06, 0xd4, 0x30,
- 0x00, 0x65, 0xc0, 0x41,
- 0x10, 0xb8, 0x64, 0x7c,
- 0xa1, 0x6a, 0xca, 0x5e,
- 0x01, 0xb4, 0x6a, 0x6c,
- 0x02, 0xb4, 0x6c, 0x6c,
- 0x01, 0xa4, 0x6c, 0x7c,
- 0xff, 0xa8, 0x7c, 0x7c,
+ 0x00, 0x65, 0xca, 0x41,
+ 0x80, 0xb8, 0x68, 0x7c,
+ 0xc0, 0x3c, 0x7c, 0x7c,
+ 0x80, 0x3c, 0x68, 0x6c,
+ 0xff, 0xa8, 0x7c, 0x6c,
+ 0x40, 0x3c, 0x68, 0x6c,
+ 0x10, 0xb8, 0x80, 0x7c,
+ 0xa1, 0x6a, 0xea, 0x5e,
+ 0x01, 0xb4, 0x86, 0x6c,
+ 0x02, 0xb4, 0x88, 0x6c,
+ 0x01, 0xa4, 0x88, 0x7c,
+ 0xff, 0xa8, 0x98, 0x7c,
0x04, 0xb4, 0x68, 0x01,
0x01, 0x6a, 0x76, 0x00,
- 0x00, 0xbb, 0x04, 0x5e,
- 0xff, 0xa8, 0x7c, 0x7c,
- 0x71, 0x6a, 0xca, 0x5e,
- 0x40, 0x51, 0x7c, 0x64,
- 0x00, 0x65, 0xa4, 0x5e,
- 0x00, 0x65, 0xd2, 0x41,
- 0x00, 0xbb, 0x80, 0x5c,
- 0x00, 0x65, 0xd2, 0x41,
- 0x00, 0x65, 0xa4, 0x5e,
+ 0x00, 0xbb, 0x24, 0x5e,
+ 0xff, 0xa8, 0x98, 0x7c,
+ 0x71, 0x6a, 0xea, 0x5e,
+ 0x40, 0x51, 0x98, 0x64,
+ 0x00, 0x65, 0xc4, 0x5e,
+ 0x00, 0x65, 0xdc, 0x41,
+ 0x00, 0xbb, 0x9c, 0x5c,
+ 0x00, 0x65, 0xdc, 0x41,
+ 0x00, 0x65, 0xc4, 0x5e,
0x01, 0x65, 0xa2, 0x30,
0x01, 0xf8, 0xc8, 0x30,
0x01, 0x4e, 0xc8, 0x30,
- 0x00, 0x6a, 0xa8, 0xdd,
- 0x00, 0x51, 0xba, 0x5d,
+ 0x00, 0x6a, 0xc8, 0xdd,
+ 0x00, 0x51, 0xda, 0x5d,
0x01, 0x4e, 0x9c, 0x18,
0x02, 0x6a, 0x22, 0x05,
+ 0xc0, 0x3c, 0x68, 0x6c,
0x04, 0xb8, 0x70, 0x01,
- 0x00, 0x65, 0xc6, 0x5e,
- 0x20, 0xb8, 0xd2, 0x69,
+ 0x00, 0x65, 0xe6, 0x5e,
+ 0x20, 0xb8, 0xdc, 0x69,
0x01, 0xbb, 0xa2, 0x30,
0x01, 0xba, 0x7c, 0x30,
- 0x00, 0xb9, 0xc0, 0x5c,
- 0x00, 0x65, 0xd2, 0x41,
+ 0x00, 0xb9, 0xe0, 0x5c,
+ 0x00, 0x65, 0xdc, 0x41,
0x01, 0x06, 0xd4, 0x30,
- 0x20, 0x3c, 0xc0, 0x79,
- 0x20, 0x3c, 0x5c, 0x7c,
- 0x01, 0xa4, 0xac, 0x7c,
+ 0x20, 0x3c, 0xca, 0x79,
+ 0x20, 0x3c, 0x6e, 0x7c,
+ 0x01, 0xa4, 0xca, 0x7c,
0x01, 0xb4, 0x68, 0x01,
- 0x00, 0x65, 0xc0, 0x41,
- 0x00, 0x65, 0x5c, 0x44,
+ 0x00, 0x65, 0xca, 0x41,
+ 0x00, 0x65, 0x6e, 0x44,
0x04, 0x14, 0x58, 0x31,
0x01, 0x06, 0xd4, 0x30,
0x08, 0xa0, 0x60, 0x31,
0xac, 0x6a, 0xcc, 0x00,
- 0x14, 0x6a, 0xe6, 0x5d,
+ 0x14, 0x6a, 0x06, 0x5e,
0x01, 0x06, 0xd4, 0x30,
- 0xa0, 0x6a, 0xde, 0x5d,
- 0x00, 0x65, 0xc0, 0x41,
+ 0xa0, 0x6a, 0xfe, 0x5d,
+ 0x00, 0x65, 0xca, 0x41,
0xdf, 0x3c, 0x78, 0x08,
- 0x00, 0x65, 0x5c, 0x44,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x00, 0x65, 0x6e, 0x44,
0x4c, 0x65, 0xcc, 0x28,
0x01, 0x3e, 0x20, 0x31,
0xd0, 0x66, 0xcc, 0x18,
@@ -624,102 +640,102 @@ static uint8_t seqprog[] = {
0xd0, 0x65, 0xca, 0x18,
0x01, 0x3e, 0x20, 0x31,
0x30, 0x65, 0xd4, 0x18,
- 0x00, 0x65, 0xd8, 0x4c,
+ 0x00, 0x65, 0xf8, 0x4c,
0xe1, 0x6a, 0x22, 0x01,
0xff, 0x6a, 0xd4, 0x08,
0x20, 0x65, 0xd4, 0x18,
- 0x00, 0x65, 0xe0, 0x54,
+ 0x00, 0x65, 0x00, 0x55,
0xe1, 0x6a, 0x22, 0x01,
0xff, 0x6a, 0xd4, 0x08,
0x20, 0x65, 0xca, 0x18,
0xe0, 0x65, 0xd4, 0x18,
- 0x00, 0x65, 0xea, 0x4c,
+ 0x00, 0x65, 0x0a, 0x4d,
0xe1, 0x6a, 0x22, 0x01,
0xff, 0x6a, 0xd4, 0x08,
0xd0, 0x65, 0xd4, 0x18,
- 0x00, 0x65, 0xf2, 0x54,
+ 0x00, 0x65, 0x12, 0x55,
0xe1, 0x6a, 0x22, 0x01,
0xff, 0x6a, 0xd4, 0x08,
0x01, 0x6c, 0xa2, 0x30,
- 0xff, 0x51, 0x04, 0x75,
- 0x00, 0x51, 0x80, 0x5d,
+ 0xff, 0x51, 0x24, 0x75,
+ 0x00, 0x51, 0xa0, 0x5d,
0x01, 0x51, 0x20, 0x31,
- 0x00, 0x65, 0x26, 0x45,
+ 0x00, 0x65, 0x46, 0x45,
0x01, 0xba, 0xc8, 0x30,
- 0x00, 0x3e, 0x26, 0x75,
- 0x00, 0x65, 0xa2, 0x5e,
+ 0x00, 0x3e, 0x46, 0x75,
+ 0x00, 0x65, 0xc2, 0x5e,
0x80, 0x3c, 0x78, 0x00,
0x01, 0x06, 0xd4, 0x30,
- 0x00, 0x65, 0xca, 0x5d,
+ 0x00, 0x65, 0xea, 0x5d,
0x01, 0x3c, 0x78, 0x00,
- 0xe0, 0x3f, 0x42, 0x65,
+ 0xe0, 0x3f, 0x62, 0x65,
0x02, 0x3c, 0x78, 0x00,
- 0x20, 0x12, 0x42, 0x65,
- 0x51, 0x6a, 0x50, 0x5d,
- 0x00, 0x51, 0x80, 0x5d,
- 0x51, 0x6a, 0x50, 0x5d,
+ 0x20, 0x12, 0x62, 0x65,
+ 0x51, 0x6a, 0x70, 0x5d,
+ 0x00, 0x51, 0xa0, 0x5d,
+ 0x51, 0x6a, 0x70, 0x5d,
0x01, 0x51, 0x20, 0x31,
0x04, 0x3c, 0x78, 0x00,
0x01, 0xb9, 0xc8, 0x30,
- 0x00, 0x3d, 0x40, 0x65,
+ 0x00, 0x3d, 0x60, 0x65,
0x08, 0x3c, 0x78, 0x00,
0x01, 0xba, 0xc8, 0x30,
- 0x00, 0x3e, 0x40, 0x65,
+ 0x00, 0x3e, 0x60, 0x65,
0x10, 0x3c, 0x78, 0x00,
- 0x04, 0xb8, 0x40, 0x7d,
+ 0x04, 0xb8, 0x60, 0x7d,
0xfb, 0xb8, 0x70, 0x09,
- 0x20, 0xb8, 0x36, 0x6d,
+ 0x20, 0xb8, 0x56, 0x6d,
0x01, 0x90, 0xc8, 0x30,
0xff, 0x6a, 0xa2, 0x00,
- 0x00, 0x3d, 0xc0, 0x5c,
+ 0x00, 0x3d, 0xe0, 0x5c,
0x01, 0x64, 0x20, 0x31,
- 0x80, 0x6a, 0x78, 0x00,
- 0x00, 0x65, 0xec, 0x58,
- 0x10, 0xb8, 0x5c, 0x7c,
- 0xff, 0x6a, 0x46, 0x5d,
- 0x00, 0x65, 0x5c, 0x44,
- 0x00, 0x65, 0xa2, 0x5e,
- 0x31, 0x6a, 0xca, 0x5e,
- 0x00, 0x65, 0x5c, 0x44,
+ 0xff, 0x6a, 0x78, 0x08,
+ 0x00, 0x65, 0xea, 0x58,
+ 0x10, 0xb8, 0x6e, 0x7c,
+ 0xff, 0x6a, 0x66, 0x5d,
+ 0x00, 0x65, 0x6e, 0x44,
+ 0x00, 0x65, 0xc2, 0x5e,
+ 0x31, 0x6a, 0xea, 0x5e,
+ 0x00, 0x65, 0x6e, 0x44,
0x10, 0x3f, 0x06, 0x00,
0x10, 0x6a, 0x06, 0x00,
0x01, 0x65, 0x74, 0x34,
- 0x81, 0x6a, 0xca, 0x5e,
- 0x00, 0x65, 0x52, 0x45,
+ 0x81, 0x6a, 0xea, 0x5e,
+ 0x00, 0x65, 0x72, 0x45,
0x01, 0x06, 0xd4, 0x30,
- 0x01, 0x0c, 0x52, 0x7d,
- 0x04, 0x0c, 0x4c, 0x6d,
+ 0x01, 0x0c, 0x72, 0x7d,
+ 0x04, 0x0c, 0x6c, 0x6d,
0xe0, 0x03, 0x7e, 0x08,
- 0xe0, 0x3f, 0xc0, 0x61,
+ 0xe0, 0x3f, 0xca, 0x61,
0x01, 0x65, 0xcc, 0x30,
0x01, 0x12, 0xda, 0x34,
0x01, 0x06, 0xd4, 0x34,
- 0x01, 0x03, 0x60, 0x6d,
+ 0x01, 0x03, 0x80, 0x6d,
0x40, 0x03, 0xcc, 0x08,
0x01, 0x65, 0x06, 0x30,
0x40, 0x65, 0xc8, 0x08,
- 0x00, 0x66, 0x6e, 0x75,
- 0x40, 0x65, 0x6e, 0x7d,
- 0x00, 0x65, 0x6e, 0x5d,
+ 0x00, 0x66, 0x8e, 0x75,
+ 0x40, 0x65, 0x8e, 0x7d,
+ 0x00, 0x65, 0x8e, 0x5d,
0xff, 0x6a, 0xd4, 0x08,
0xff, 0x6a, 0xd4, 0x08,
0xff, 0x6a, 0xd4, 0x08,
0xff, 0x6a, 0xd4, 0x0c,
0x08, 0x01, 0x02, 0x00,
- 0x02, 0x0b, 0x78, 0x7d,
+ 0x02, 0x0b, 0x98, 0x7d,
0x01, 0x65, 0x0c, 0x30,
- 0x02, 0x0b, 0x7c, 0x7d,
+ 0x02, 0x0b, 0x9c, 0x7d,
0xf7, 0x01, 0x02, 0x0c,
0x01, 0x65, 0xc8, 0x30,
- 0xff, 0x41, 0xa0, 0x75,
+ 0xff, 0x41, 0xc0, 0x75,
0x01, 0x41, 0x20, 0x31,
0xff, 0x6a, 0xa4, 0x00,
- 0x00, 0x65, 0x90, 0x45,
- 0xff, 0xbf, 0xa0, 0x75,
+ 0x00, 0x65, 0xb0, 0x45,
+ 0xff, 0xbf, 0xc0, 0x75,
0x01, 0x90, 0xa4, 0x30,
0x01, 0xbf, 0x20, 0x31,
- 0x00, 0xbb, 0x8a, 0x65,
- 0xff, 0x52, 0x9e, 0x75,
+ 0x00, 0xbb, 0xaa, 0x65,
+ 0xff, 0x52, 0xbe, 0x75,
0x01, 0xbf, 0xcc, 0x30,
0x01, 0x90, 0xca, 0x30,
0x01, 0x52, 0x20, 0x31,
@@ -727,28 +743,28 @@ static uint8_t seqprog[] = {
0x01, 0x65, 0x20, 0x35,
0x01, 0xbf, 0x82, 0x34,
0x01, 0x64, 0xa2, 0x30,
- 0x00, 0x6a, 0xb2, 0x5e,
+ 0x00, 0x6a, 0xd2, 0x5e,
0x0d, 0x6a, 0x76, 0x00,
- 0x00, 0x51, 0x04, 0x46,
+ 0x00, 0x51, 0x24, 0x46,
0x01, 0x65, 0xa4, 0x30,
0xe0, 0x6a, 0xcc, 0x00,
- 0x48, 0x6a, 0xf8, 0x5d,
+ 0x48, 0x6a, 0x18, 0x5e,
0x01, 0x6a, 0xd0, 0x01,
0x01, 0x6a, 0xdc, 0x05,
0x88, 0x6a, 0xcc, 0x00,
- 0x48, 0x6a, 0xf8, 0x5d,
- 0x01, 0x6a, 0xd2, 0x5d,
+ 0x48, 0x6a, 0x18, 0x5e,
+ 0x01, 0x6a, 0xf2, 0x5d,
0x01, 0x6a, 0x26, 0x05,
0x01, 0x65, 0xd8, 0x31,
0x09, 0xee, 0xdc, 0x01,
- 0x80, 0xee, 0xbe, 0x7d,
+ 0x80, 0xee, 0xde, 0x7d,
0xff, 0x6a, 0xdc, 0x0d,
0x01, 0x65, 0x32, 0x31,
0x0a, 0x93, 0x26, 0x01,
- 0x00, 0x65, 0x9a, 0x46,
- 0x81, 0x6a, 0xca, 0x5e,
- 0x01, 0x0c, 0xca, 0x7d,
- 0x04, 0x0c, 0xc8, 0x6d,
+ 0x00, 0x65, 0xba, 0x46,
+ 0x81, 0x6a, 0xea, 0x5e,
+ 0x01, 0x0c, 0xea, 0x7d,
+ 0x04, 0x0c, 0xe8, 0x6d,
0xe0, 0x03, 0x06, 0x08,
0xe0, 0x03, 0x7e, 0x0c,
0x01, 0x65, 0x18, 0x31,
@@ -767,7 +783,7 @@ static uint8_t seqprog[] = {
0x01, 0x6c, 0xda, 0x34,
0x3d, 0x64, 0xa4, 0x28,
0x55, 0x64, 0xc8, 0x28,
- 0x00, 0x65, 0xf8, 0x45,
+ 0x00, 0x65, 0x18, 0x46,
0x2e, 0x64, 0xa4, 0x28,
0x66, 0x64, 0xc8, 0x28,
0x00, 0x6c, 0xda, 0x18,
@@ -778,63 +794,63 @@ static uint8_t seqprog[] = {
0x00, 0x6c, 0xda, 0x24,
0x01, 0x65, 0xc8, 0x30,
0xe0, 0x6a, 0xcc, 0x00,
- 0x44, 0x6a, 0xf4, 0x5d,
+ 0x44, 0x6a, 0x14, 0x5e,
0x01, 0x90, 0xe2, 0x31,
- 0x04, 0x3b, 0x18, 0x7e,
+ 0x04, 0x3b, 0x38, 0x7e,
0x30, 0x6a, 0xd0, 0x01,
0x20, 0x6a, 0xd0, 0x01,
0x1d, 0x6a, 0xdc, 0x01,
- 0xdc, 0xee, 0x14, 0x66,
- 0x00, 0x65, 0x30, 0x46,
+ 0xdc, 0xee, 0x34, 0x66,
+ 0x00, 0x65, 0x50, 0x46,
0x20, 0x6a, 0xd0, 0x01,
0x01, 0x6a, 0xdc, 0x01,
0x20, 0xa0, 0xd8, 0x31,
0x09, 0xee, 0xdc, 0x01,
- 0x80, 0xee, 0x20, 0x7e,
+ 0x80, 0xee, 0x40, 0x7e,
0x11, 0x6a, 0xdc, 0x01,
- 0x50, 0xee, 0x24, 0x66,
+ 0x50, 0xee, 0x44, 0x66,
0x20, 0x6a, 0xd0, 0x01,
0x09, 0x6a, 0xdc, 0x01,
- 0x88, 0xee, 0x2a, 0x66,
+ 0x88, 0xee, 0x4a, 0x66,
0x19, 0x6a, 0xdc, 0x01,
- 0xd8, 0xee, 0x2e, 0x66,
+ 0xd8, 0xee, 0x4e, 0x66,
0xff, 0x6a, 0xdc, 0x09,
- 0x18, 0xee, 0x32, 0x6e,
+ 0x18, 0xee, 0x52, 0x6e,
0xff, 0x6a, 0xd4, 0x0c,
0x88, 0x6a, 0xcc, 0x00,
- 0x44, 0x6a, 0xf4, 0x5d,
- 0x20, 0x6a, 0xd2, 0x5d,
+ 0x44, 0x6a, 0x14, 0x5e,
+ 0x20, 0x6a, 0xf2, 0x5d,
0x01, 0x3b, 0x26, 0x31,
- 0x04, 0x3b, 0x4c, 0x6e,
+ 0x04, 0x3b, 0x6c, 0x6e,
0xa0, 0x6a, 0xca, 0x00,
0x20, 0x65, 0xc8, 0x18,
- 0x00, 0x65, 0x8a, 0x5e,
- 0x00, 0x65, 0x44, 0x66,
+ 0x00, 0x65, 0xaa, 0x5e,
+ 0x00, 0x65, 0x64, 0x66,
0x0a, 0x93, 0x26, 0x01,
- 0x00, 0x65, 0x9a, 0x46,
+ 0x00, 0x65, 0xba, 0x46,
0xa0, 0x6a, 0xcc, 0x00,
0xff, 0x6a, 0xc8, 0x08,
- 0x20, 0x94, 0x50, 0x6e,
- 0x10, 0x94, 0x52, 0x6e,
- 0x08, 0x94, 0x6c, 0x6e,
- 0x08, 0x94, 0x6c, 0x6e,
- 0x08, 0x94, 0x6c, 0x6e,
+ 0x20, 0x94, 0x70, 0x6e,
+ 0x10, 0x94, 0x72, 0x6e,
+ 0x08, 0x94, 0x8c, 0x6e,
+ 0x08, 0x94, 0x8c, 0x6e,
+ 0x08, 0x94, 0x8c, 0x6e,
0xff, 0x8c, 0xc8, 0x10,
0xc1, 0x64, 0xc8, 0x18,
0xf8, 0x64, 0xc8, 0x08,
0x01, 0x99, 0xda, 0x30,
- 0x00, 0x66, 0x60, 0x66,
- 0xc0, 0x66, 0x9c, 0x76,
+ 0x00, 0x66, 0x80, 0x66,
+ 0xc0, 0x66, 0xbc, 0x76,
0x60, 0x66, 0xc8, 0x18,
0x3d, 0x64, 0xc8, 0x28,
- 0x00, 0x65, 0x50, 0x46,
+ 0x00, 0x65, 0x70, 0x46,
0xf7, 0x93, 0x26, 0x09,
- 0x08, 0x93, 0x6e, 0x6e,
+ 0x08, 0x93, 0x8e, 0x6e,
0x00, 0x62, 0xc4, 0x18,
+ 0x00, 0x65, 0xba, 0x5e,
+ 0x00, 0x65, 0x9a, 0x5e,
+ 0x00, 0x65, 0x9a, 0x5e,
0x00, 0x65, 0x9a, 0x5e,
- 0x00, 0x65, 0x7a, 0x5e,
- 0x00, 0x65, 0x7a, 0x5e,
- 0x00, 0x65, 0x7a, 0x5e,
0x01, 0x99, 0xda, 0x30,
0x01, 0x99, 0xda, 0x30,
0x01, 0x99, 0xda, 0x30,
@@ -851,11 +867,11 @@ static uint8_t seqprog[] = {
0x01, 0x6c, 0x32, 0x31,
0x01, 0x6c, 0x32, 0x31,
0x01, 0x6c, 0x32, 0x35,
- 0x08, 0x94, 0x9a, 0x7e,
+ 0x08, 0x94, 0xba, 0x7e,
0xf7, 0x93, 0x26, 0x09,
- 0x08, 0x93, 0x9e, 0x6e,
+ 0x08, 0x93, 0xbe, 0x6e,
0xff, 0x6a, 0xd4, 0x0c,
- 0x04, 0xb8, 0xc6, 0x6e,
+ 0x04, 0xb8, 0xe6, 0x6e,
0x01, 0x42, 0x7e, 0x31,
0xff, 0x6a, 0x76, 0x01,
0x01, 0x90, 0x84, 0x34,
@@ -863,14 +879,14 @@ static uint8_t seqprog[] = {
0x01, 0x85, 0x0a, 0x01,
0x7f, 0x65, 0x10, 0x09,
0xfe, 0x85, 0x0a, 0x0d,
- 0xff, 0x42, 0xc2, 0x66,
- 0xff, 0x41, 0xba, 0x66,
- 0xd1, 0x6a, 0xca, 0x5e,
+ 0xff, 0x42, 0xe2, 0x66,
+ 0xff, 0x41, 0xda, 0x66,
+ 0xd1, 0x6a, 0xea, 0x5e,
0xff, 0x6a, 0xca, 0x04,
0x01, 0x41, 0x20, 0x31,
0x01, 0xbf, 0x82, 0x30,
0x01, 0x6a, 0x76, 0x00,
- 0x00, 0xbb, 0x04, 0x46,
+ 0x00, 0xbb, 0x24, 0x46,
0x01, 0x42, 0x20, 0x31,
0x01, 0xbf, 0x84, 0x34,
0x01, 0x41, 0x7e, 0x31,
@@ -880,7 +896,8 @@ static uint8_t seqprog[] = {
0xff, 0x6a, 0xd4, 0x0c
};
-static int ahc_patch23_func(struct ahc_softc *ahc);
+typedef int ahc_patch_func_t (struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch23_func;
static int
ahc_patch23_func(struct ahc_softc *ahc)
@@ -888,7 +905,7 @@ ahc_patch23_func(struct ahc_softc *ahc)
return ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0);
}
-static int ahc_patch22_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch22_func;
static int
ahc_patch22_func(struct ahc_softc *ahc)
@@ -896,7 +913,7 @@ ahc_patch22_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_CMD_CHAN) == 0);
}
-static int ahc_patch21_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch21_func;
static int
ahc_patch21_func(struct ahc_softc *ahc)
@@ -904,7 +921,7 @@ ahc_patch21_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_QUEUE_REGS) == 0);
}
-static int ahc_patch20_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch20_func;
static int
ahc_patch20_func(struct ahc_softc *ahc)
@@ -912,7 +929,7 @@ ahc_patch20_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_WIDE) != 0);
}
-static int ahc_patch19_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch19_func;
static int
ahc_patch19_func(struct ahc_softc *ahc)
@@ -920,7 +937,7 @@ ahc_patch19_func(struct ahc_softc *ahc)
return ((ahc->flags & AHC_SCB_BTT) != 0);
}
-static int ahc_patch18_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch18_func;
static int
ahc_patch18_func(struct ahc_softc *ahc)
@@ -928,7 +945,7 @@ ahc_patch18_func(struct ahc_softc *ahc)
return ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0);
}
-static int ahc_patch17_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch17_func;
static int
ahc_patch17_func(struct ahc_softc *ahc)
@@ -936,7 +953,7 @@ ahc_patch17_func(struct ahc_softc *ahc)
return ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0);
}
-static int ahc_patch16_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch16_func;
static int
ahc_patch16_func(struct ahc_softc *ahc)
@@ -944,7 +961,7 @@ ahc_patch16_func(struct ahc_softc *ahc)
return ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0);
}
-static int ahc_patch15_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch15_func;
static int
ahc_patch15_func(struct ahc_softc *ahc)
@@ -952,7 +969,7 @@ ahc_patch15_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_ULTRA2) == 0);
}
-static int ahc_patch14_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch14_func;
static int
ahc_patch14_func(struct ahc_softc *ahc)
@@ -960,7 +977,7 @@ ahc_patch14_func(struct ahc_softc *ahc)
return ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0);
}
-static int ahc_patch13_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch13_func;
static int
ahc_patch13_func(struct ahc_softc *ahc)
@@ -968,7 +985,7 @@ ahc_patch13_func(struct ahc_softc *ahc)
return ((ahc->flags & AHC_39BIT_ADDRESSING) != 0);
}
-static int ahc_patch12_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch12_func;
static int
ahc_patch12_func(struct ahc_softc *ahc)
@@ -976,7 +993,7 @@ ahc_patch12_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_HS_MAILBOX) != 0);
}
-static int ahc_patch11_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch11_func;
static int
ahc_patch11_func(struct ahc_softc *ahc)
@@ -984,7 +1001,7 @@ ahc_patch11_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_ULTRA) != 0);
}
-static int ahc_patch10_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch10_func;
static int
ahc_patch10_func(struct ahc_softc *ahc)
@@ -992,7 +1009,7 @@ ahc_patch10_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_MULTI_TID) != 0);
}
-static int ahc_patch9_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch9_func;
static int
ahc_patch9_func(struct ahc_softc *ahc)
@@ -1000,7 +1017,7 @@ ahc_patch9_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_CMD_CHAN) != 0);
}
-static int ahc_patch8_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch8_func;
static int
ahc_patch8_func(struct ahc_softc *ahc)
@@ -1008,7 +1025,7 @@ ahc_patch8_func(struct ahc_softc *ahc)
return ((ahc->flags & AHC_INITIATORROLE) != 0);
}
-static int ahc_patch7_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch7_func;
static int
ahc_patch7_func(struct ahc_softc *ahc)
@@ -1016,7 +1033,7 @@ ahc_patch7_func(struct ahc_softc *ahc)
return ((ahc->flags & AHC_TARGETROLE) != 0);
}
-static int ahc_patch6_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch6_func;
static int
ahc_patch6_func(struct ahc_softc *ahc)
@@ -1024,7 +1041,7 @@ ahc_patch6_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_DT) == 0);
}
-static int ahc_patch5_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch5_func;
static int
ahc_patch5_func(struct ahc_softc *ahc)
@@ -1032,7 +1049,7 @@ ahc_patch5_func(struct ahc_softc *ahc)
return ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0);
}
-static int ahc_patch4_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch4_func;
static int
ahc_patch4_func(struct ahc_softc *ahc)
@@ -1040,7 +1057,7 @@ ahc_patch4_func(struct ahc_softc *ahc)
return ((ahc->flags & AHC_PAGESCBS) != 0);
}
-static int ahc_patch3_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch3_func;
static int
ahc_patch3_func(struct ahc_softc *ahc)
@@ -1048,7 +1065,7 @@ ahc_patch3_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_QUEUE_REGS) != 0);
}
-static int ahc_patch2_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch2_func;
static int
ahc_patch2_func(struct ahc_softc *ahc)
@@ -1056,7 +1073,7 @@ ahc_patch2_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_TWIN) != 0);
}
-static int ahc_patch1_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch1_func;
static int
ahc_patch1_func(struct ahc_softc *ahc)
@@ -1064,7 +1081,7 @@ ahc_patch1_func(struct ahc_softc *ahc)
return ((ahc->features & AHC_ULTRA2) != 0);
}
-static int ahc_patch0_func(struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch0_func;
static int
ahc_patch0_func(struct ahc_softc *ahc)
@@ -1072,12 +1089,11 @@ ahc_patch0_func(struct ahc_softc *ahc)
return (0);
}
-typedef int patch_func_t (struct ahc_softc *);
-struct patch {
- patch_func_t *patch_func;
- uint32_t begin :10,
- skip_instr :10,
- skip_patch :12;
+static struct patch {
+ ahc_patch_func_t *patch_func;
+ uint32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
} patches[] = {
{ ahc_patch1_func, 4, 1, 1 },
{ ahc_patch2_func, 6, 2, 1 },
@@ -1120,180 +1136,181 @@ struct patch {
{ ahc_patch0_func, 111, 1, 1 },
{ ahc_patch7_func, 112, 4, 2 },
{ ahc_patch0_func, 116, 1, 1 },
- { ahc_patch11_func, 118, 2, 1 },
- { ahc_patch1_func, 120, 1, 2 },
- { ahc_patch0_func, 121, 1, 1 },
- { ahc_patch7_func, 122, 4, 1 },
- { ahc_patch7_func, 133, 89, 11 },
+ { ahc_patch11_func, 117, 2, 1 },
+ { ahc_patch1_func, 119, 1, 2 },
+ { ahc_patch0_func, 120, 1, 1 },
+ { ahc_patch7_func, 121, 4, 1 },
+ { ahc_patch7_func, 131, 94, 11 },
{ ahc_patch4_func, 151, 1, 1 },
- { ahc_patch1_func, 164, 1, 1 },
- { ahc_patch12_func, 169, 1, 2 },
- { ahc_patch0_func, 170, 1, 1 },
- { ahc_patch9_func, 181, 1, 2 },
- { ahc_patch0_func, 182, 1, 1 },
- { ahc_patch9_func, 191, 1, 2 },
- { ahc_patch0_func, 192, 1, 1 },
- { ahc_patch9_func, 208, 6, 2 },
- { ahc_patch0_func, 214, 6, 1 },
- { ahc_patch8_func, 222, 18, 2 },
- { ahc_patch1_func, 235, 1, 1 },
- { ahc_patch1_func, 242, 1, 2 },
- { ahc_patch0_func, 243, 2, 2 },
- { ahc_patch11_func, 244, 1, 1 },
- { ahc_patch9_func, 252, 31, 3 },
- { ahc_patch1_func, 268, 14, 2 },
- { ahc_patch13_func, 273, 1, 1 },
- { ahc_patch14_func, 283, 14, 1 },
- { ahc_patch1_func, 299, 1, 2 },
- { ahc_patch0_func, 300, 1, 1 },
- { ahc_patch9_func, 303, 1, 1 },
- { ahc_patch13_func, 308, 1, 1 },
- { ahc_patch9_func, 309, 2, 2 },
- { ahc_patch0_func, 311, 4, 1 },
- { ahc_patch14_func, 315, 1, 1 },
- { ahc_patch15_func, 318, 2, 3 },
- { ahc_patch9_func, 318, 1, 2 },
- { ahc_patch0_func, 319, 1, 1 },
- { ahc_patch6_func, 324, 1, 2 },
- { ahc_patch0_func, 325, 1, 1 },
- { ahc_patch1_func, 329, 50, 11 },
- { ahc_patch6_func, 338, 2, 4 },
- { ahc_patch7_func, 338, 1, 1 },
- { ahc_patch8_func, 339, 1, 1 },
- { ahc_patch0_func, 340, 1, 1 },
- { ahc_patch16_func, 341, 1, 1 },
- { ahc_patch6_func, 360, 6, 3 },
- { ahc_patch16_func, 360, 5, 1 },
- { ahc_patch0_func, 366, 5, 1 },
- { ahc_patch13_func, 374, 5, 1 },
- { ahc_patch0_func, 379, 54, 17 },
- { ahc_patch14_func, 379, 1, 1 },
- { ahc_patch7_func, 381, 2, 2 },
- { ahc_patch17_func, 382, 1, 1 },
- { ahc_patch9_func, 385, 1, 1 },
- { ahc_patch18_func, 392, 1, 1 },
- { ahc_patch14_func, 397, 9, 3 },
- { ahc_patch9_func, 398, 3, 2 },
- { ahc_patch0_func, 401, 3, 1 },
- { ahc_patch9_func, 409, 6, 2 },
- { ahc_patch0_func, 415, 9, 2 },
- { ahc_patch13_func, 415, 1, 1 },
- { ahc_patch13_func, 424, 2, 1 },
- { ahc_patch14_func, 426, 1, 1 },
- { ahc_patch9_func, 428, 1, 2 },
- { ahc_patch0_func, 429, 1, 1 },
- { ahc_patch7_func, 432, 1, 1 },
- { ahc_patch7_func, 433, 1, 1 },
- { ahc_patch8_func, 434, 3, 3 },
- { ahc_patch6_func, 435, 1, 2 },
- { ahc_patch0_func, 436, 1, 1 },
- { ahc_patch9_func, 437, 1, 1 },
- { ahc_patch15_func, 438, 1, 2 },
- { ahc_patch13_func, 438, 1, 1 },
- { ahc_patch14_func, 440, 9, 4 },
- { ahc_patch9_func, 440, 1, 1 },
- { ahc_patch9_func, 447, 2, 1 },
- { ahc_patch0_func, 449, 4, 3 },
- { ahc_patch9_func, 449, 1, 2 },
- { ahc_patch0_func, 450, 3, 1 },
- { ahc_patch1_func, 454, 2, 1 },
- { ahc_patch7_func, 456, 10, 2 },
- { ahc_patch0_func, 466, 1, 1 },
- { ahc_patch8_func, 467, 109, 23 },
- { ahc_patch1_func, 469, 3, 2 },
- { ahc_patch0_func, 472, 5, 3 },
- { ahc_patch9_func, 472, 2, 2 },
- { ahc_patch0_func, 474, 3, 1 },
- { ahc_patch1_func, 479, 2, 2 },
- { ahc_patch0_func, 481, 6, 3 },
- { ahc_patch9_func, 481, 2, 2 },
- { ahc_patch0_func, 483, 3, 1 },
- { ahc_patch1_func, 489, 2, 2 },
- { ahc_patch0_func, 491, 9, 7 },
- { ahc_patch9_func, 491, 5, 6 },
- { ahc_patch19_func, 491, 1, 2 },
- { ahc_patch0_func, 492, 1, 1 },
- { ahc_patch19_func, 494, 1, 2 },
- { ahc_patch0_func, 495, 1, 1 },
- { ahc_patch0_func, 496, 4, 1 },
- { ahc_patch6_func, 500, 3, 2 },
- { ahc_patch0_func, 503, 1, 1 },
- { ahc_patch1_func, 506, 1, 1 },
- { ahc_patch6_func, 512, 1, 2 },
- { ahc_patch0_func, 513, 1, 1 },
- { ahc_patch20_func, 550, 7, 1 },
- { ahc_patch3_func, 578, 1, 2 },
- { ahc_patch0_func, 579, 1, 1 },
- { ahc_patch21_func, 582, 1, 1 },
- { ahc_patch8_func, 584, 104, 33 },
- { ahc_patch4_func, 585, 1, 1 },
- { ahc_patch1_func, 591, 2, 2 },
+ { ahc_patch1_func, 167, 1, 1 },
+ { ahc_patch12_func, 172, 1, 2 },
+ { ahc_patch0_func, 173, 1, 1 },
+ { ahc_patch9_func, 184, 1, 2 },
+ { ahc_patch0_func, 185, 1, 1 },
+ { ahc_patch9_func, 194, 1, 2 },
+ { ahc_patch0_func, 195, 1, 1 },
+ { ahc_patch9_func, 211, 6, 2 },
+ { ahc_patch0_func, 217, 6, 1 },
+ { ahc_patch8_func, 225, 20, 2 },
+ { ahc_patch1_func, 240, 1, 1 },
+ { ahc_patch1_func, 247, 1, 2 },
+ { ahc_patch0_func, 248, 2, 2 },
+ { ahc_patch11_func, 249, 1, 1 },
+ { ahc_patch9_func, 257, 31, 3 },
+ { ahc_patch1_func, 273, 14, 2 },
+ { ahc_patch13_func, 278, 1, 1 },
+ { ahc_patch14_func, 288, 14, 1 },
+ { ahc_patch1_func, 304, 1, 2 },
+ { ahc_patch0_func, 305, 1, 1 },
+ { ahc_patch9_func, 308, 1, 1 },
+ { ahc_patch13_func, 313, 1, 1 },
+ { ahc_patch9_func, 314, 2, 2 },
+ { ahc_patch0_func, 316, 4, 1 },
+ { ahc_patch14_func, 320, 1, 1 },
+ { ahc_patch15_func, 323, 2, 3 },
+ { ahc_patch9_func, 323, 1, 2 },
+ { ahc_patch0_func, 324, 1, 1 },
+ { ahc_patch6_func, 329, 1, 2 },
+ { ahc_patch0_func, 330, 1, 1 },
+ { ahc_patch1_func, 334, 50, 11 },
+ { ahc_patch6_func, 343, 2, 4 },
+ { ahc_patch7_func, 343, 1, 1 },
+ { ahc_patch8_func, 344, 1, 1 },
+ { ahc_patch0_func, 345, 1, 1 },
+ { ahc_patch16_func, 346, 1, 1 },
+ { ahc_patch6_func, 365, 6, 3 },
+ { ahc_patch16_func, 365, 5, 1 },
+ { ahc_patch0_func, 371, 5, 1 },
+ { ahc_patch13_func, 379, 5, 1 },
+ { ahc_patch0_func, 384, 54, 17 },
+ { ahc_patch14_func, 384, 1, 1 },
+ { ahc_patch7_func, 386, 2, 2 },
+ { ahc_patch17_func, 387, 1, 1 },
+ { ahc_patch9_func, 390, 1, 1 },
+ { ahc_patch18_func, 397, 1, 1 },
+ { ahc_patch14_func, 402, 9, 3 },
+ { ahc_patch9_func, 403, 3, 2 },
+ { ahc_patch0_func, 406, 3, 1 },
+ { ahc_patch9_func, 414, 6, 2 },
+ { ahc_patch0_func, 420, 9, 2 },
+ { ahc_patch13_func, 420, 1, 1 },
+ { ahc_patch13_func, 429, 2, 1 },
+ { ahc_patch14_func, 431, 1, 1 },
+ { ahc_patch9_func, 433, 1, 2 },
+ { ahc_patch0_func, 434, 1, 1 },
+ { ahc_patch7_func, 437, 1, 1 },
+ { ahc_patch7_func, 438, 1, 1 },
+ { ahc_patch8_func, 439, 3, 3 },
+ { ahc_patch6_func, 440, 1, 2 },
+ { ahc_patch0_func, 441, 1, 1 },
+ { ahc_patch9_func, 442, 1, 1 },
+ { ahc_patch15_func, 443, 1, 2 },
+ { ahc_patch13_func, 443, 1, 1 },
+ { ahc_patch14_func, 445, 9, 4 },
+ { ahc_patch9_func, 445, 1, 1 },
+ { ahc_patch9_func, 452, 2, 1 },
+ { ahc_patch0_func, 454, 4, 3 },
+ { ahc_patch9_func, 454, 1, 2 },
+ { ahc_patch0_func, 455, 3, 1 },
+ { ahc_patch1_func, 459, 2, 1 },
+ { ahc_patch7_func, 461, 10, 2 },
+ { ahc_patch0_func, 471, 1, 1 },
+ { ahc_patch8_func, 472, 118, 22 },
+ { ahc_patch1_func, 474, 3, 2 },
+ { ahc_patch0_func, 477, 5, 3 },
+ { ahc_patch9_func, 477, 2, 2 },
+ { ahc_patch0_func, 479, 3, 1 },
+ { ahc_patch1_func, 484, 2, 2 },
+ { ahc_patch0_func, 486, 6, 3 },
+ { ahc_patch9_func, 486, 2, 2 },
+ { ahc_patch0_func, 488, 3, 1 },
+ { ahc_patch1_func, 494, 2, 2 },
+ { ahc_patch0_func, 496, 9, 7 },
+ { ahc_patch9_func, 496, 5, 6 },
+ { ahc_patch19_func, 496, 1, 2 },
+ { ahc_patch0_func, 497, 1, 1 },
+ { ahc_patch19_func, 499, 1, 2 },
+ { ahc_patch0_func, 500, 1, 1 },
+ { ahc_patch0_func, 501, 4, 1 },
+ { ahc_patch6_func, 506, 3, 2 },
+ { ahc_patch0_func, 509, 1, 1 },
+ { ahc_patch6_func, 519, 1, 2 },
+ { ahc_patch0_func, 520, 1, 1 },
+ { ahc_patch20_func, 557, 7, 1 },
+ { ahc_patch3_func, 592, 1, 2 },
{ ahc_patch0_func, 593, 1, 1 },
- { ahc_patch1_func, 596, 1, 2 },
- { ahc_patch0_func, 597, 1, 1 },
- { ahc_patch9_func, 598, 3, 3 },
- { ahc_patch15_func, 599, 1, 1 },
- { ahc_patch0_func, 601, 4, 1 },
- { ahc_patch19_func, 609, 2, 2 },
- { ahc_patch0_func, 611, 1, 1 },
- { ahc_patch19_func, 615, 10, 3 },
- { ahc_patch5_func, 617, 8, 1 },
- { ahc_patch0_func, 625, 9, 2 },
- { ahc_patch5_func, 626, 8, 1 },
- { ahc_patch4_func, 636, 1, 2 },
- { ahc_patch0_func, 637, 1, 1 },
- { ahc_patch19_func, 638, 1, 2 },
- { ahc_patch0_func, 639, 3, 2 },
- { ahc_patch4_func, 641, 1, 1 },
- { ahc_patch5_func, 642, 1, 1 },
- { ahc_patch5_func, 645, 1, 1 },
- { ahc_patch5_func, 647, 1, 1 },
- { ahc_patch4_func, 649, 2, 2 },
- { ahc_patch0_func, 651, 2, 1 },
- { ahc_patch5_func, 653, 1, 1 },
- { ahc_patch5_func, 656, 1, 1 },
- { ahc_patch5_func, 659, 1, 1 },
- { ahc_patch19_func, 663, 1, 1 },
- { ahc_patch19_func, 666, 1, 1 },
- { ahc_patch4_func, 672, 1, 1 },
- { ahc_patch6_func, 675, 1, 2 },
- { ahc_patch0_func, 676, 1, 1 },
- { ahc_patch7_func, 688, 16, 1 },
- { ahc_patch4_func, 704, 20, 1 },
- { ahc_patch9_func, 725, 4, 2 },
- { ahc_patch0_func, 729, 4, 1 },
- { ahc_patch9_func, 733, 4, 2 },
- { ahc_patch0_func, 737, 3, 1 },
- { ahc_patch6_func, 743, 1, 1 },
- { ahc_patch22_func, 745, 14, 1 },
- { ahc_patch7_func, 759, 3, 1 },
- { ahc_patch9_func, 771, 24, 8 },
- { ahc_patch19_func, 775, 1, 2 },
- { ahc_patch0_func, 776, 1, 1 },
- { ahc_patch15_func, 781, 4, 2 },
- { ahc_patch0_func, 785, 7, 3 },
- { ahc_patch23_func, 785, 5, 2 },
- { ahc_patch0_func, 790, 2, 1 },
- { ahc_patch0_func, 795, 42, 3 },
- { ahc_patch18_func, 807, 18, 2 },
- { ahc_patch0_func, 825, 1, 1 },
- { ahc_patch4_func, 849, 1, 1 },
- { ahc_patch4_func, 850, 3, 2 },
- { ahc_patch0_func, 853, 1, 1 },
- { ahc_patch13_func, 854, 3, 1 },
- { ahc_patch4_func, 857, 12, 1 }
+ { ahc_patch21_func, 596, 1, 1 },
+ { ahc_patch8_func, 598, 106, 33 },
+ { ahc_patch4_func, 600, 1, 1 },
+ { ahc_patch1_func, 606, 2, 2 },
+ { ahc_patch0_func, 608, 1, 1 },
+ { ahc_patch1_func, 611, 1, 2 },
+ { ahc_patch0_func, 612, 1, 1 },
+ { ahc_patch9_func, 613, 3, 3 },
+ { ahc_patch15_func, 614, 1, 1 },
+ { ahc_patch0_func, 616, 4, 1 },
+ { ahc_patch19_func, 625, 2, 2 },
+ { ahc_patch0_func, 627, 1, 1 },
+ { ahc_patch19_func, 631, 10, 3 },
+ { ahc_patch5_func, 633, 8, 1 },
+ { ahc_patch0_func, 641, 9, 2 },
+ { ahc_patch5_func, 642, 8, 1 },
+ { ahc_patch4_func, 652, 1, 2 },
+ { ahc_patch0_func, 653, 1, 1 },
+ { ahc_patch19_func, 654, 1, 2 },
+ { ahc_patch0_func, 655, 3, 2 },
+ { ahc_patch4_func, 657, 1, 1 },
+ { ahc_patch5_func, 658, 1, 1 },
+ { ahc_patch5_func, 661, 1, 1 },
+ { ahc_patch5_func, 663, 1, 1 },
+ { ahc_patch4_func, 665, 2, 2 },
+ { ahc_patch0_func, 667, 2, 1 },
+ { ahc_patch5_func, 669, 1, 1 },
+ { ahc_patch5_func, 672, 1, 1 },
+ { ahc_patch5_func, 675, 1, 1 },
+ { ahc_patch19_func, 679, 1, 1 },
+ { ahc_patch19_func, 682, 1, 1 },
+ { ahc_patch4_func, 688, 1, 1 },
+ { ahc_patch6_func, 691, 1, 2 },
+ { ahc_patch0_func, 692, 1, 1 },
+ { ahc_patch7_func, 704, 16, 1 },
+ { ahc_patch4_func, 720, 20, 1 },
+ { ahc_patch9_func, 741, 4, 2 },
+ { ahc_patch0_func, 745, 4, 1 },
+ { ahc_patch9_func, 749, 4, 2 },
+ { ahc_patch0_func, 753, 3, 1 },
+ { ahc_patch6_func, 759, 1, 1 },
+ { ahc_patch22_func, 761, 14, 1 },
+ { ahc_patch7_func, 775, 3, 1 },
+ { ahc_patch9_func, 787, 24, 8 },
+ { ahc_patch19_func, 791, 1, 2 },
+ { ahc_patch0_func, 792, 1, 1 },
+ { ahc_patch15_func, 797, 4, 2 },
+ { ahc_patch0_func, 801, 7, 3 },
+ { ahc_patch23_func, 801, 5, 2 },
+ { ahc_patch0_func, 806, 2, 1 },
+ { ahc_patch0_func, 811, 42, 3 },
+ { ahc_patch18_func, 823, 18, 2 },
+ { ahc_patch0_func, 841, 1, 1 },
+ { ahc_patch4_func, 865, 1, 1 },
+ { ahc_patch4_func, 866, 3, 2 },
+ { ahc_patch0_func, 869, 1, 1 },
+ { ahc_patch13_func, 870, 3, 1 },
+ { ahc_patch4_func, 873, 12, 1 }
};
-struct cs {
- u_int16_t begin;
- u_int16_t end;
+
+static struct cs {
+ uint16_t begin;
+ uint16_t end;
} critical_sections[] = {
{ 11, 18 },
{ 21, 30 },
- { 704, 720 },
- { 850, 853 },
- { 857, 863 },
- { 865, 867 },
- { 867, 869 }
+ { 720, 736 },
+ { 866, 869 },
+ { 873, 879 },
+ { 881, 883 },
+ { 883, 885 }
};
-const int num_critical_sections = sizeof(critical_sections)
- / sizeof(*critical_sections);
+
+static const int num_critical_sections = sizeof(critical_sections)
+ / sizeof(*critical_sections);
diff --git a/drivers/scsi/aic7xxx/aicasm/Makefile b/drivers/scsi/aic7xxx/aicasm/Makefile
index 1446a9c76575..16394e1f8f5a 100644
--- a/drivers/scsi/aic7xxx/aicasm/Makefile
+++ b/drivers/scsi/aic7xxx/aicasm/Makefile
@@ -1,15 +1,16 @@
PROG= aicasm
-.SUFFIXES= .l .y .c
+.SUFFIXES= .l .y .c .h
CSRCS= aicasm.c aicasm_symbol.c
-GENSRCS= aicasm_gram.c aicasm_scan.c
-GENHDRS= y.tab.h aicdb.h
+YSRCS= aicasm_gram.y aicasm_macro_gram.y
+LSRCS= aicasm_scan.l aicasm_macro_scan.l
-SRCS= ${GENSRCS} ${CSRCS}
+GENHDRS= aicdb.h $(YSRCS:.y=.h)
+GENSRCS= $(YSRCS:.y=.c) $(LSRCS:.l=.c)
-# Cleaned up by make clean
-clean-files := $(GENSRCS) $(GENHDRS) y.output $(PROG)
+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
@@ -28,7 +29,7 @@ YFLAGS+= -t -v
LFLAGS= -d
endif
-$(PROG): $(SRCS) $(GENHDRS)
+$(PROG): ${GENHDRS} $(SRCS)
$(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG)
aicdb.h:
@@ -44,8 +45,21 @@ aicdb.h:
echo "*** Install db development libraries"; \
fi
-y.tab.h aicasm_gram.c: aicasm_gram.y
- $(YACC) $(YFLAGS) aicasm_gram.y
- mv y.tab.c aicasm_gram.c
+clean:
+ rm -f $(CLEANFILES) $(PROG)
-aicasm_scan.c: y.tab.h
+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/drivers/scsi/aic7xxx/aicasm/aicasm.c b/drivers/scsi/aic7xxx/aicasm/aicasm.c
index bb645b9d2928..c34639481904 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm.c
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.c
@@ -2,7 +2,7 @@
* Aic7xxx SCSI host adapter firmware asssembler
*
* Copyright (c) 1997, 1998, 2000, 2001 Justin T. Gibbs.
- * Copyright (c) 2001 Adaptec Inc.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,15 +37,16 @@
* 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#11 $
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.c#22 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm.c,v 1.29 2000/10/05 04:25:42 gibbs Exp $
+ * $FreeBSD$
*/
#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>
@@ -84,12 +85,17 @@ static int check_patch(patch_t **start_patch, int start_instr,
struct path_list search_path;
int includes_search_curdir;
char *appname;
+char *stock_include_file;
FILE *ofile;
char *ofilename;
char *regfilename;
FILE *regfile;
char *listfilename;
FILE *listfile;
+char *regdiagfilename;
+FILE *regdiagfile;
+int src_mode;
+int dst_mode;
static STAILQ_HEAD(,instruction) seq_program;
struct cs_tailq cs_tailq;
@@ -98,7 +104,9 @@ 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);
@@ -131,16 +139,20 @@ main(int argc, char *argv[])
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) {
+ while ((ch = getopt(argc, argv, "d:i:l:n:o:p:r:I:")) != -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);
@@ -151,6 +163,9 @@ main(int argc, char *argv[])
"information", EX_SOFTWARE);
#endif
break;
+ case 'i':
+ stock_include_file = optarg;
+ break;
case 'l':
/* Create a program listing */
if ((listfile = fopen(optarg, "w")) == NULL) {
@@ -175,6 +190,14 @@ main(int argc, char *argv[])
}
ofilename = optarg;
break;
+ case 'p':
+ /* Create Register Diagnostic "printing" Functions */
+ if ((regdiagfile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ regdiagfilename = optarg;
+ break;
case 'r':
if ((regfile = fopen(optarg, "w")) == NULL) {
perror(optarg);
@@ -236,6 +259,14 @@ main(int argc, char *argv[])
/* NOTREACHED */
}
+ if (regdiagfile != NULL
+ && (regfile == NULL || stock_include_file == NULL)) {
+ fprintf(stderr,
+ "%s: The -p option requires the -r and -i options.\n",
+ appname);
+ usage();
+ /* NOTREACHED */
+ }
symtable_open();
inputfilename = *argv;
include_file(*argv, SOURCE_FILE);
@@ -243,8 +274,7 @@ main(int argc, char *argv[])
if (retval == 0) {
if (SLIST_FIRST(&scope_stack) == NULL
|| SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) {
- stop("Unterminated conditional expression",
- EX_DATAERR);
+ stop("Unterminated conditional expression", EX_DATAERR);
/* NOTREACHED */
}
@@ -263,9 +293,8 @@ main(int argc, char *argv[])
if (ofile != NULL)
output_code();
- if (regfile != NULL) {
- symtable_dump(regfile);
- }
+ if (regfile != NULL)
+ symtable_dump(regfile, regdiagfile);
if (listfile != NULL)
output_listing(inputfilename);
}
@@ -280,10 +309,10 @@ 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);
+"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]\n"
+" [-r register_output_file [-p register_diag_file -i includefile]]\n"
+" [-l program_list_file]\n"
+" input_file\n", appname);
exit(EX_USAGE);
}
@@ -327,11 +356,11 @@ output_code()
instrcount = 0;
fprintf(ofile,
-"/*
- * DO NOT EDIT - This file is automatically generated
- * from the following source files:
- *
-%s */\n", versions);
+"/*\n"
+" * DO NOT EDIT - This file is automatically generated\n"
+" * from the following source files:\n"
+" *\n"
+"%s */\n", versions);
fprintf(ofile, "static uint8_t seqprog[] = {\n");
for (cur_instr = STAILQ_FIRST(&seq_program);
@@ -355,64 +384,75 @@ output_code()
}
fprintf(ofile, "\n};\n\n");
+ if (patch_arg_list == NULL)
+ stop("Patch argument list not defined",
+ EX_DATAERR);
+
/*
* Output patch information. Patch functions first.
*/
+ fprintf(ofile,
+"typedef int %spatch_func_t (%s);\n", prefix, patch_arg_list);
+
for (cur_node = SLIST_FIRST(&patch_functions);
cur_node != NULL;
cur_node = SLIST_NEXT(cur_node,links)) {
fprintf(ofile,
-"static int ahc_patch%d_func(struct ahc_softc *ahc);
-
-static int
-ahc_patch%d_func(struct ahc_softc *ahc)
-{
- return (%s);
-}\n\n",
+"static %spatch_func_t %spatch%d_func;\n"
+"\n"
+"static int\n"
+"%spatch%d_func(%s)\n"
+"{\n"
+" return (%s);\n"
+"}\n\n",
+ prefix,
+ prefix,
cur_node->symbol->info.condinfo->func_num,
+ prefix,
cur_node->symbol->info.condinfo->func_num,
+ patch_arg_list,
cur_node->symbol->name);
}
fprintf(ofile,
-"typedef int patch_func_t (struct ahc_softc *);
-struct patch {
- patch_func_t *patch_func;
- uint32_t begin :10,
- skip_instr :10,
- skip_patch :12;
-} patches[] = {\n");
-
- for(cur_patch = STAILQ_FIRST(&patches);
- cur_patch != NULL;
- cur_patch = STAILQ_NEXT(cur_patch,links)) {
- fprintf(ofile, "%s\t{ ahc_patch%d_func, %d, %d, %d }",
+"static struct patch {\n"
+" %spatch_func_t *patch_func;\n"
+" uint32_t begin :10,\n"
+" skip_instr :10,\n"
+" skip_patch :12;\n"
+"} patches[] = {\n", prefix);
+
+ for (cur_patch = STAILQ_FIRST(&patches);
+ cur_patch != NULL;
+ cur_patch = STAILQ_NEXT(cur_patch,links)) {
+ fprintf(ofile, "%s\t{ %spatch%d_func, %d, %d, %d }",
cur_patch == STAILQ_FIRST(&patches) ? "" : ",\n",
+ prefix,
cur_patch->patch_func, cur_patch->begin,
cur_patch->skip_instr, cur_patch->skip_patch);
}
- fprintf(ofile, "\n};\n");
+ fprintf(ofile, "\n};\n\n");
fprintf(ofile,
-"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)) {
+"static struct cs {\n"
+" uint16_t begin;\n"
+" uint16_t end;\n"
+"} 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, "\n};\n\n");
fprintf(ofile,
-"const int num_critical_sections = sizeof(critical_sections)
- / sizeof(*critical_sections);\n");
+"static const int num_critical_sections = sizeof(critical_sections)\n"
+" / sizeof(*critical_sections);\n");
fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
}
@@ -555,9 +595,9 @@ output_listing(char *ifilename)
/* 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++) {
+ 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) {
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm.h b/drivers/scsi/aic7xxx/aicasm/aicasm.h
index b4e4560accd0..51678dd46ff7 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm.h
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.h
@@ -2,7 +2,7 @@
* Assembler for the sequencer program downloaded to Aic7xxx SCSI host adapters
*
* Copyright (c) 1997 Justin T. Gibbs.
- * Copyright (c) 2001 Adaptec Inc.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,9 +37,9 @@
* 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#7 $
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.h#14 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm.h,v 1.11 2000/09/22 22:19:54 gibbs Exp $
+ * $FreeBSD$
*/
#ifdef __linux__
@@ -76,12 +76,19 @@ 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 char *stock_include_file;
extern int yylineno;
extern char *yyfilename;
+extern char *prefix;
+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);
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
index 871d2209db62..69749944b326 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
@@ -3,7 +3,7 @@
* Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
*
* Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
- * Copyright (c) 2001 Adaptec Inc.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -38,18 +38,20 @@
* 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#9 $
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_gram.y#28 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_gram.y,v 1.12 2000/10/31 18:44:32 gibbs Exp $
+ * $FreeBSD$
*/
+#include <sys/types.h>
+
#include <inttypes.h>
+#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
-#include <sys/types.h>
#ifdef __linux__
#include "../queue.h"
#else
@@ -62,21 +64,34 @@
int yylineno;
char *yyfilename;
+char stock_prefix[] = "aic_";
+char *prefix = stock_prefix;
+char *patch_arg_list;
char *versions;
+static char errbuf[255];
+static char regex_pattern[255];
static symbol_t *cur_symbol;
+static symbol_t *field_symbol;
+static symbol_t *scb_or_sram_symbol;
static symtype cur_symtype;
-static symbol_t *accumulator;
+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 u_int enum_increment;
+static u_int enum_next_value;
-static void process_bitmask(int mask_type, symbol_t *sym, int mask);
+static void process_field(int field_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);
@@ -92,13 +107,12 @@ static void add_conditional(symbol_t *symbol);
static void add_version(const char *verstring);
static int is_download_const(expression_t *immed);
-#define YYDEBUG 1
#define SRAM_SYMNAME "SRAM_BASE"
#define SCB_SYMNAME "SCB_BASE"
%}
%union {
- int value;
+ u_int value;
char *str;
symbol_t *sym;
symbol_ref_t sym_ref;
@@ -109,6 +123,8 @@ static int is_download_const(expression_t *immed);
%token <value> T_CONST
+%token T_EXPORT
+
%token T_DOWNLOAD
%token T_SCB
@@ -119,27 +135,41 @@ static int is_download_const(expression_t *immed);
%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_FIELD
+
+%token T_ENUM
%token T_MASK
%token <value> T_NUMBER
-%token <str> T_PATH T_STRING
+%token <str> T_PATH T_STRING T_ARG T_MACROBODY
%token <sym> T_CEXPR
-%token T_EOF T_INCLUDE T_VERSION
+%token T_EOF T_INCLUDE T_VERSION T_PREFIX T_PATCH_ARG_LIST
%token <value> T_SHR T_SHL T_ROR T_ROL
@@ -163,7 +193,7 @@ static int is_download_const(expression_t *immed);
%token T_NOP
-%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX
+%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX T_MODE_PTR
%token T_A
@@ -177,13 +207,15 @@ static int is_download_const(expression_t *immed);
%type <expression> expression immediate immediate_or_a
-%type <value> ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne
+%type <value> export ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne
-%type <value> numerical_value
+%type <value> numerical_value mode_value mode_list macro_arglist
%left '|'
%left '&'
+%left T_EXPR_LSHIFT T_EXPR_RSHIFT
%left '+' '-'
+%left '*' '/'
%right '~'
%nonassoc UMINUS
%%
@@ -191,18 +223,28 @@ static int is_download_const(expression_t *immed);
program:
include
| program include
+| prefix
+| program prefix
+| 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
@@ -224,6 +266,30 @@ include:
}
;
+prefix:
+ T_PREFIX '=' T_STRING
+ {
+ if (prefix != stock_prefix)
+ stop("Prefix multiply defined",
+ EX_DATAERR);
+ prefix = strdup($3);
+ if (prefix == NULL)
+ stop("Unable to record prefix", EX_SOFTWARE);
+ }
+;
+
+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); }
@@ -280,10 +346,13 @@ reg_attribute:
reg_address
| size
| access_mode
-| bit_defn
+| modes
+| field_defn
+| enum_defn
| mask_defn
| alias
| accumulator
+| mode_pointer
| allones
| allzeros
| none
@@ -301,6 +370,18 @@ 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);
+ }
}
;
@@ -311,17 +392,116 @@ access_mode:
}
;
-bit_defn:
- T_BIT T_SYMBOL T_NUMBER
+modes:
+ T_MODES mode_list
{
- process_bitmask(BIT, $2, $3);
+ 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);
+ }
+;
+
+field_defn:
+ T_FIELD
+ {
+ field_symbol = NULL;
+ enum_next_value = 0;
+ enum_increment = 1;
+ }
+ '{' enum_entry_list '}'
+| T_FIELD T_SYMBOL expression
+ {
+ process_field(FIELD, $2, $3.value);
+ field_symbol = $2;
+ enum_next_value = 0;
+ enum_increment = 0x01 << (ffs($3.value) - 1);
+ }
+ '{' enum_entry_list '}'
+| T_FIELD T_SYMBOL expression
+ {
+ process_field(FIELD, $2, $3.value);
+ }
+;
+
+enum_defn:
+ T_ENUM
+ {
+ field_symbol = NULL;
+ enum_next_value = 0;
+ enum_increment = 1;
+ }
+ '{' enum_entry_list '}'
+| T_ENUM T_SYMBOL expression
+ {
+ process_field(ENUM, $2, $3.value);
+ field_symbol = $2;
+ enum_next_value = 0;
+ enum_increment = 0x01 << (ffs($3.value) - 1);
+ }
+ '{' enum_entry_list '}'
+;
+
+enum_entry_list:
+ enum_entry
+| enum_entry_list ',' enum_entry
+;
+
+enum_entry:
+ T_SYMBOL
+ {
+ process_field(ENUM_ENTRY, $1, enum_next_value);
+ enum_next_value += enum_increment;
+ }
+| T_SYMBOL expression
+ {
+ process_field(ENUM_ENTRY, $1, $2.value);
+ enum_next_value = $2.value + enum_increment;
}
;
mask_defn:
T_MASK T_SYMBOL expression
{
- process_bitmask(MASK, $2, $3.value);
+ process_field(MASK, $2, $3.value);
}
;
@@ -342,12 +522,24 @@ alias:
accumulator:
T_ACCUM
{
- if (accumulator != NULL) {
+ if (accumulator.symbol != NULL) {
stop("Only one accumulator definition allowed",
EX_DATAERR);
/* NOTREACHED */
}
- accumulator = cur_symbol;
+ 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;
}
;
@@ -428,6 +620,34 @@ expression:
&($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;
@@ -461,8 +681,10 @@ expression:
$$.value = symbol->info.rinfo->address;
break;
case MASK:
- case BIT:
- $$.value = symbol->info.minfo->mask;
+ case FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ $$.value = symbol->info.finfo->value;
break;
case DOWNLOAD_CONST:
case CONST:
@@ -471,12 +693,10 @@ expression:
case UNINITIALIZED:
default:
{
- char buf[255];
-
- snprintf(buf, sizeof(buf),
+ snprintf(errbuf, sizeof(errbuf),
"Undefined symbol %s referenced",
symbol->name);
- stop(buf, EX_DATAERR);
+ stop(errbuf, EX_DATAERR);
/* NOTREACHED */
break;
}
@@ -487,7 +707,7 @@ expression:
;
constant:
- T_CONST T_SYMBOL numerical_value
+ T_CONST T_SYMBOL expression
{
if ($2->type != UNINITIALIZED) {
stop("Re-definition of symbol as a constant",
@@ -496,8 +716,7 @@ constant:
}
$2->type = CONST;
initialize_symbol($2);
- $2->info.cinfo->value = $3;
- $2->info.cinfo->define = $1;
+ $2->info.cinfo->value = $3.value;
}
| T_CONST T_SYMBOL T_DOWNLOAD
{
@@ -514,7 +733,54 @@ constant:
$2->type = DOWNLOAD_CONST;
initialize_symbol($2);
$2->info.cinfo->value = download_constant_count++;
- $2->info.cinfo->define = FALSE;
+ }
+;
+
+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);
}
;
@@ -532,13 +798,10 @@ numerical_value:
scratch_ram:
T_SRAM '{'
{
+ snprintf(errbuf, sizeof(errbuf), "%s%d", SRAM_SYMNAME,
+ num_srams);
cur_symbol = symtable_get(SRAM_SYMNAME);
cur_symtype = SRAMLOC;
- if (cur_symbol->type != UNINITIALIZED) {
- stop("Only one SRAM definition allowed",
- EX_DATAERR);
- /* NOTREACHED */
- }
cur_symbol->type = SRAMLOC;
initialize_symbol(cur_symbol);
}
@@ -546,10 +809,15 @@ scratch_ram:
{
sram_or_scb_offset = cur_symbol->info.rinfo->address;
}
- scb_or_sram_reg_list
+ size
+ {
+ scb_or_sram_symbol = cur_symbol;
+ }
+ scb_or_sram_attributes
'}'
{
cur_symbol = NULL;
+ scb_or_sram_symbol = NULL;
}
;
@@ -572,13 +840,25 @@ scb:
{
sram_or_scb_offset = cur_symbol->info.rinfo->address;
}
- scb_or_sram_reg_list
+ 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
@@ -619,11 +899,11 @@ reg_symbol:
}
| T_A
{
- if (accumulator == NULL) {
+ if (accumulator.symbol == NULL) {
stop("No accumulator has been defined", EX_DATAERR);
/* NOTREACHED */
}
- $$.symbol = accumulator;
+ $$.symbol = accumulator.symbol;
$$.offset = 0;
}
;
@@ -644,11 +924,21 @@ immediate:
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);
+ symlist_add(&$$.referenced_syms, accumulator.symbol,
+ SYMLIST_INSERT_HEAD);
$$.value = 0;
}
;
@@ -676,8 +966,22 @@ 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
+ T_BEGIN_CS ';'
{
critical_section_t *cs;
@@ -690,9 +994,10 @@ critical_section_start:
cs->begin_addr = instruction_ptr;
in_critical_section = TRUE;
}
+;
critical_section_end:
- T_END_CS
+ T_END_CS ';'
{
critical_section_t *cs;
@@ -704,17 +1009,25 @@ critical_section_end:
cs->end_addr = instruction_ptr;
in_critical_section = FALSE;
}
+;
+
+export:
+ { $$ = 0; }
+| T_EXPORT
+ { $$ = 1; }
+;
label:
- T_SYMBOL ':'
+ export T_SYMBOL ':'
{
- if ($1->type != UNINITIALIZED) {
+ if ($2->type != UNINITIALIZED) {
stop("Program label multiply defined", EX_DATAERR);
/* NOTREACHED */
}
- $1->type = LABEL;
- initialize_symbol($1);
- $1->info.linfo->address = instruction_ptr;
+ $2->type = LABEL;
+ initialize_symbol($2);
+ $2->info.linfo->address = instruction_ptr;
+ $2->info.linfo->exported = $1;
}
;
@@ -927,9 +1240,22 @@ code:
;
code:
- T_MVI destination ',' immediate_or_a ret ';'
+ T_MVI destination ',' immediate ret ';'
{
- format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5);
+ if ($4.value == 0
+ && is_download_const(&$4) == 0) {
+ expression_t immed;
+
+ /*
+ * Allow move immediates of 0 so that macros,
+ * that can't know the immediate's value and
+ * otherwise compensate, still work.
+ */
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_BMOV, &$2, &immed, &allzeros, $5);
+ } else {
+ format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5);
+ }
}
;
@@ -1064,7 +1390,7 @@ code:
%%
static void
-process_bitmask(int mask_type, symbol_t *sym, int mask)
+process_field(int field_type, symbol_t *sym, int value)
{
/*
* Add the current register to its
@@ -1074,52 +1400,54 @@ process_bitmask(int mask_type, symbol_t *sym, int mask)
* the "allowed bits" of this register.
*/
if (sym->type == UNINITIALIZED) {
- sym->type = mask_type;
+ sym->type = field_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);
+ sym->info.finfo->value = value;
+ if (field_type != ENUM_ENTRY) {
+ if (field_type != MASK && value == 0) {
+ stop("Empty Field, or Enum", EX_DATAERR);
/* NOTREACHED */
}
+ sym->info.finfo->value = value;
+ sym->info.finfo->mask = value;
+ } else if (field_symbol != NULL) {
+ sym->info.finfo->mask = field_symbol->info.finfo->value;
+ } else {
+ sym->info.finfo->mask = 0xFF;
}
- sym->info.minfo->mask = mask;
- } else if (sym->type != mask_type) {
- stop("Bit definition mirrors a definition of the same "
+ } else if (sym->type != field_type) {
+ stop("Field 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);
+ } else if (value != sym->info.finfo->value) {
+ stop("Field redefined with a conflicting value", EX_DATAERR);
/* NOTREACHED */
}
/* Fail if this symbol is already listed */
- if (symlist_search(&(sym->info.minfo->symrefs),
+ if (symlist_search(&(sym->info.finfo->symrefs),
cur_symbol->name) != NULL) {
- stop("Bitmask defined multiple times for register", EX_DATAERR);
+ stop("Field defined multiple times for register", EX_DATAERR);
/* NOTREACHED */
}
- symlist_add(&(sym->info.minfo->symrefs), cur_symbol,
+ symlist_add(&(sym->info.finfo->symrefs), cur_symbol,
SYMLIST_INSERT_HEAD);
- cur_symbol->info.rinfo->valid_bitmask |= mask;
+ cur_symbol->info.rinfo->valid_bitmask |= sym->info.finfo->mask;
cur_symbol->info.rinfo->typecheck_masks = TRUE;
+ symlist_add(&(cur_symbol->info.rinfo->fields), sym, SYMLIST_SORT);
}
static void
initialize_symbol(symbol_t *symbol)
{
switch (symbol->type) {
- case UNINITIALIZED:
+ case UNINITIALIZED:
stop("Call to initialize_symbol with type field unset",
EX_SOFTWARE);
/* NOTREACHED */
break;
- case REGISTER:
- case SRAMLOC:
- case SCBLOC:
+ case REGISTER:
+ case SRAMLOC:
+ case SCBLOC:
symbol->info.rinfo =
(struct reg_info *)malloc(sizeof(struct reg_info));
if (symbol->info.rinfo == NULL) {
@@ -1128,8 +1456,19 @@ initialize_symbol(symbol_t *symbol)
}
memset(symbol->info.rinfo, 0,
sizeof(struct reg_info));
+ SLIST_INIT(&(symbol->info.rinfo->fields));
+ /*
+ * 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:
+ case ALIAS:
symbol->info.ainfo =
(struct alias_info *)malloc(sizeof(struct alias_info));
if (symbol->info.ainfo == NULL) {
@@ -1139,19 +1478,21 @@ initialize_symbol(symbol_t *symbol)
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);
+ case MASK:
+ case FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ symbol->info.finfo =
+ (struct field_info *)malloc(sizeof(struct field_info));
+ if (symbol->info.finfo == NULL) {
+ stop("Can't create field info", EX_SOFTWARE);
/* NOTREACHED */
}
- memset(symbol->info.minfo, 0, sizeof(struct mask_info));
- SLIST_INIT(&(symbol->info.minfo->symrefs));
+ memset(symbol->info.finfo, 0, sizeof(struct field_info));
+ SLIST_INIT(&(symbol->info.finfo->symrefs));
break;
- case CONST:
- case DOWNLOAD_CONST:
+ case CONST:
+ case DOWNLOAD_CONST:
symbol->info.cinfo =
(struct const_info *)malloc(sizeof(struct const_info));
if (symbol->info.cinfo == NULL) {
@@ -1181,6 +1522,17 @@ initialize_symbol(symbol_t *symbol)
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);
@@ -1190,25 +1542,75 @@ initialize_symbol(symbol_t *symbol)
}
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)
{
- char buf[255];
symbol_t *symbol = *p_symbol;
if (symbol->type == UNINITIALIZED) {
- snprintf(buf, sizeof(buf), "Undefined register %s",
+ snprintf(errbuf, sizeof(errbuf), "Undefined register %s",
symbol->name);
- stop(buf, EX_DATAERR);
+ 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(buf, sizeof(buf),
+ snprintf(errbuf, sizeof(errbuf),
"Specified symbol %s is not a register",
symbol->name);
- stop(buf, EX_DATAERR);
+ stop(errbuf, EX_DATAERR);
}
}
@@ -1242,7 +1644,47 @@ format_1_instr(int opcode, symbol_ref_t *dest, expression_t *immed,
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;
+ 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++;
}
@@ -1350,6 +1792,14 @@ format_3_instr(int opcode, symbol_ref_t *src,
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);
@@ -1360,6 +1810,14 @@ test_readable_symbol(symbol_t *symbol)
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);
@@ -1372,7 +1830,6 @@ type_check(symbol_t *symbol, expression_t *expression, int opcode)
{
symbol_node_t *node;
int and_op;
- char buf[255];
and_op = FALSE;
if (opcode == AIC_OP_AND || opcode == AIC_OP_JNZ || AIC_OP_JZ)
@@ -1385,11 +1842,11 @@ type_check(symbol_t *symbol, expression_t *expression, int opcode)
*/
if (and_op == FALSE
&& (expression->value & ~symbol->info.rinfo->valid_bitmask) != 0) {
- snprintf(buf, sizeof(buf),
+ snprintf(errbuf, sizeof(errbuf),
"Invalid bit(s) 0x%x in immediate written to %s",
expression->value & ~symbol->info.rinfo->valid_bitmask,
symbol->name);
- stop(buf, EX_DATAERR);
+ stop(errbuf, EX_DATAERR);
/* NOTREACHED */
}
@@ -1397,19 +1854,21 @@ type_check(symbol_t *symbol, expression_t *expression, int opcode)
* Now make sure that all of the symbols referenced by the
* expression are defined for this register.
*/
- if(symbol->info.rinfo->typecheck_masks != FALSE) {
+ 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,
+ || node->symbol->type == FIELD
+ || node->symbol->type == ENUM
+ || node->symbol->type == ENUM_ENTRY)
+ && symlist_search(&node->symbol->info.finfo->symrefs,
symbol->name) == NULL) {
- snprintf(buf, sizeof(buf),
- "Invalid bit or mask %s "
+ snprintf(errbuf, sizeof(errbuf),
+ "Invalid field or mask %s "
"for register %s",
node->symbol->name, symbol->name);
- stop(buf, EX_DATAERR);
+ stop(errbuf, EX_DATAERR);
/* NOTREACHED */
}
}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h b/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
index 98ce60129d8b..3e80f07df49c 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
@@ -37,9 +37,9 @@
* 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#7 $
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_insformat.h#11 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_insformat.h,v 1.3 2000/09/22 22:19:54 gibbs Exp $
+ * $FreeBSD$
*/
struct ins_format1 {
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
new file mode 100644
index 000000000000..439f760b34b5
--- /dev/null
+++ b/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: //depot/aic7xxx/aic7xxx/aicasm/aicasm_macro_gram.y#5 $
+ *
+ * $FreeBSD$
+ */
+
+#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/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
new file mode 100644
index 000000000000..d781443f56a3
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
@@ -0,0 +1,155 @@
+%{
+/*
+ * 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: //depot/aic7xxx/aic7xxx/aicasm/aicasm_macro_scan.l#7 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.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/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
index 1a4f8db26080..e4dc6fd359ba 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
@@ -3,7 +3,7 @@
* Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
*
* Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
- * Copyright (c) 2001 Adaptec Inc.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -38,14 +38,16 @@
* 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#7 $
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_scan.l#18 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_scan.l,v 1.13 2000/09/22 22:19:54 gibbs Exp $
+ * $FreeBSD$
*/
#include <sys/types.h>
+#include <inttypes.h>
#include <limits.h>
+#include <regex.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
@@ -57,23 +59,31 @@
#include "aicasm.h"
#include "aicasm_symbol.h"
-#include "y.tab.h"
-
-#define MAX_STR_CONST 256
-char string_buf[MAX_STR_CONST];
-char *string_buf_ptr;
-int parren_count;
-int quote_count;
+#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_.]*[./][-/A-Za-z0-9_.]*
+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; }
@@ -122,6 +132,8 @@ if[ \t]*\( {
}
VERSION { return T_VERSION; }
+PREFIX { return T_PREFIX; }
+PATCH_ARG_LIST { return T_PATCH_ARG_LIST; }
\" {
string_buf_ptr = string_buf;
BEGIN STRING;
@@ -140,14 +152,16 @@ VERSION { return T_VERSION; }
yylval.str = string_buf;
return T_STRING;
}
-{SPACE} ;
+{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;
@@ -159,13 +173,17 @@ RW|RO|WO {
}
BEGIN_CRITICAL { return T_BEGIN_CS; }
END_CRITICAL { return T_END_CS; }
-bit { return T_BIT; }
+SET_SRC_MODE { return T_SET_SRC_MODE; }
+SET_DST_MODE { return T_SET_DST_MODE; }
+field { return T_FIELD; }
+enum { return T_ENUM; }
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; }
@@ -206,7 +224,9 @@ nop { return T_NOP; }
else { return T_ELSE; }
/* Allowed Symbols */
-[-+,:()~|&."{};<>[\]!=] { return yytext[0]; }
+\<\< { return T_EXPR_LSHIFT; }
+\>\> { return T_EXPR_RSHIFT; }
+[-+,:()~|&."{};<>[\]/*!=] { return yytext[0]; }
/* Number processing */
0[0-7]* {
@@ -223,7 +243,6 @@ else { return T_ELSE; }
yylval.value = strtol(yytext, NULL, 10);
return T_NUMBER;
}
-
/* Include Files */
#include{SPACE} {
BEGIN INCLUDE;
@@ -238,13 +257,7 @@ else { return T_ELSE; }
quote_count++;
return yytext[0];
}
-<INCLUDE>. { stop("Invalid include line", EX_DATAERR); }
-
- /* For parsing C include files with #define foo */
-#define { yylval.value = TRUE; return T_CONST; }
- /* Throw away macros */
-#define[^\n]*[()]+[^\n]* ;
-<INITIAL,INCLUDE>{PATH} {
+<INCLUDE>{PATH} {
char *yptr;
yptr = yytext;
@@ -255,12 +268,158 @@ else { return T_ELSE; }
*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;
-{WORD} { yylval.sym = symtable_get(yytext); return T_SYMBOL; }
+ /* 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;
-. {
- char buf[255];
+ 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);
@@ -329,6 +488,92 @@ include_file(char *file_name, include_type type)
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()
{
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
index 562d0c91150b..2e766b85c1aa 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
@@ -2,6 +2,7 @@
* Aic7xxx SCSI host adapter firmware asssembler symbol table implementation
*
* Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -36,9 +37,9 @@
* 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#9 $
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.c#23 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_symbol.c,v 1.11 2000/09/22 22:19:54 gibbs Exp $
+ * $FreeBSD$
*/
#include <sys/types.h>
@@ -49,6 +50,8 @@
#include <db.h>
#endif
#include <fcntl.h>
+#include <inttypes.h>
+#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -71,6 +74,8 @@ symbol_create(char *name)
}
memset(new_symbol, 0, sizeof(*new_symbol));
new_symbol->name = strdup(name);
+ if (new_symbol->name == NULL)
+ stop("Unable to strdup symbol name", EX_SOFTWARE);
new_symbol->type = UNINITIALIZED;
return (new_symbol);
}
@@ -97,10 +102,12 @@ symbol_delete(symbol_t *symbol)
free(symbol->info.ainfo);
break;
case MASK:
- case BIT:
- if (symbol->info.minfo != NULL) {
- symlist_free(&symbol->info.minfo->symrefs);
- free(symbol->info.minfo);
+ case FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ if (symbol->info.finfo != NULL) {
+ symlist_free(&symbol->info.finfo->symrefs);
+ free(symbol->info.finfo);
}
break;
case DOWNLOAD_CONST:
@@ -221,17 +228,19 @@ symlist_add(symlist_t *symlist, symbol_t *symbol, int how)
newnode->symbol = symbol;
if (how == SYMLIST_SORT) {
symbol_node_t *curnode;
- int mask;
+ int field;
- mask = FALSE;
+ field = FALSE;
switch(symbol->type) {
case REGISTER:
case SCBLOC:
case SRAMLOC:
break;
- case BIT:
+ case FIELD:
case MASK:
- mask = TRUE;
+ case ENUM:
+ case ENUM_ENTRY:
+ field = TRUE;
break;
default:
stop("symlist_add: Invalid symbol type for sorting",
@@ -241,9 +250,12 @@ symlist_add(symlist_t *symlist, symbol_t *symbol, int how)
curnode = SLIST_FIRST(symlist);
if (curnode == NULL
- || (mask && (curnode->symbol->info.minfo->mask >
- newnode->symbol->info.minfo->mask))
- || (!mask && (curnode->symbol->info.rinfo->address >
+ || (field
+ && (curnode->symbol->type > newnode->symbol->type
+ || (curnode->symbol->type == newnode->symbol->type
+ && (curnode->symbol->info.finfo->value >
+ newnode->symbol->info.finfo->value))))
+ || (!field && (curnode->symbol->info.rinfo->address >
newnode->symbol->info.rinfo->address))) {
SLIST_INSERT_HEAD(symlist, newnode, links);
return;
@@ -258,10 +270,14 @@ symlist_add(symlist_t *symlist, symbol_t *symbol, int how)
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))){
+ if ((field
+ && (cursymbol->type > symbol->type
+ || (cursymbol->type == symbol->type
+ && (cursymbol->info.finfo->value >
+ symbol->info.finfo->value))))
+ || (!field
+ && (cursymbol->info.rinfo->address >
+ symbol->info.rinfo->address))) {
SLIST_INSERT_AFTER(curnode, newnode,
links);
break;
@@ -306,173 +322,356 @@ symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
}
void
-symtable_dump(FILE *ofile)
+aic_print_file_prologue(FILE *ofile)
+{
+
+ if (ofile == NULL)
+ return;
+
+ fprintf(ofile,
+"/*\n"
+" * DO NOT EDIT - This file is automatically generated\n"
+" * from the following source files:\n"
+" *\n"
+"%s */\n",
+ versions);
+}
+
+void
+aic_print_include(FILE *dfile, char *include_file)
+{
+
+ if (dfile == NULL)
+ return;
+ fprintf(dfile, "\n#include \"%s\"\n\n", include_file);
+}
+
+void
+aic_print_reg_dump_types(FILE *ofile)
+{
+ if (ofile == NULL)
+ return;
+
+ fprintf(ofile,
+"typedef int (%sreg_print_t)(u_int, u_int *, u_int);\n"
+"typedef struct %sreg_parse_entry {\n"
+" char *name;\n"
+" uint8_t value;\n"
+" uint8_t mask;\n"
+"} %sreg_parse_entry_t;\n"
+"\n",
+ prefix, prefix, prefix);
+}
+
+static void
+aic_print_reg_dump_start(FILE *dfile, symbol_node_t *regnode)
+{
+ if (dfile == NULL)
+ return;
+
+ fprintf(dfile,
+"static %sreg_parse_entry_t %s_parse_table[] = {\n",
+ prefix,
+ regnode->symbol->name);
+}
+
+static void
+aic_print_reg_dump_end(FILE *ofile, FILE *dfile,
+ symbol_node_t *regnode, u_int num_entries)
+{
+ char *lower_name;
+ char *letter;
+
+ lower_name = strdup(regnode->symbol->name);
+ if (lower_name == NULL)
+ stop("Unable to strdup symbol name", EX_SOFTWARE);
+
+ for (letter = lower_name; *letter != '\0'; letter++)
+ *letter = tolower(*letter);
+
+ if (dfile != NULL) {
+ if (num_entries != 0)
+ fprintf(dfile,
+"\n"
+"};\n"
+"\n");
+
+ fprintf(dfile,
+"int\n"
+"%s%s_print(u_int regvalue, u_int *cur_col, u_int wrap)\n"
+"{\n"
+" return (%sprint_register(%s%s, %d, \"%s\",\n"
+" 0x%02x, regvalue, cur_col, wrap));\n"
+"}\n"
+"\n",
+ prefix,
+ lower_name,
+ prefix,
+ num_entries != 0 ? regnode->symbol->name : "NULL",
+ num_entries != 0 ? "_parse_table" : "",
+ num_entries,
+ regnode->symbol->name,
+ regnode->symbol->info.rinfo->address);
+ }
+
+ fprintf(ofile,
+"#if AIC_DEBUG_REGISTERS\n"
+"%sreg_print_t %s%s_print;\n"
+"#else\n"
+"#define %s%s_print(regvalue, cur_col, wrap) \\\n"
+" %sprint_register(NULL, 0, \"%s\", 0x%02x, regvalue, cur_col, wrap)\n"
+"#endif\n"
+"\n",
+ prefix,
+ prefix,
+ lower_name,
+ prefix,
+ lower_name,
+ prefix,
+ regnode->symbol->name,
+ regnode->symbol->info.rinfo->address);
+}
+
+static void
+aic_print_reg_dump_entry(FILE *dfile, symbol_node_t *curnode)
+{
+ int num_tabs;
+
+ if (dfile == NULL)
+ return;
+
+ fprintf(dfile,
+" { \"%s\",",
+ curnode->symbol->name);
+
+ num_tabs = 3 - (strlen(curnode->symbol->name) + 5) / 8;
+
+ while (num_tabs-- > 0)
+ fputc('\t', dfile);
+ fprintf(dfile, "0x%02x, 0x%02x }",
+ curnode->symbol->info.finfo->value,
+ curnode->symbol->info.finfo->mask);
+}
+
+void
+symtable_dump(FILE *ofile, FILE *dfile)
{
/*
* 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 registers;
+ symlist_t masks;
+ symlist_t constants;
+ symlist_t download_constants;
+ symlist_t aliases;
+ symlist_t exported_labels;
+ symbol_node_t *curnode;
+ symbol_node_t *regnode;
+ DBT key;
+ DBT data;
+ int flag;
+ u_int i;
+
+ if (symtable == NULL)
+ return;
SLIST_INIT(&registers);
SLIST_INIT(&masks);
SLIST_INIT(&constants);
SLIST_INIT(&download_constants);
SLIST_INIT(&aliases);
+ SLIST_INIT(&exported_labels);
+ flag = R_FIRST;
+ while (symtable->seq(symtable, &key, &data, flag) == 0) {
+ symbol_t *cursym;
- if (symtable != NULL) {
- DBT key;
- DBT data;
- int flag = R_FIRST;
+ 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 FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ 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;
+ }
- while (symtable->seq(symtable, &key, &data, flag) == 0) {
- symbol_t *cursym;
+ /* Register dianostic functions/declarations first. */
+ aic_print_file_prologue(ofile);
+ aic_print_reg_dump_types(ofile);
+ aic_print_file_prologue(dfile);
+ aic_print_include(dfile, stock_include_file);
+ SLIST_FOREACH(curnode, &registers, links) {
- 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:
- if (cursym->info.cinfo->define == FALSE) {
- 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;
- default:
- break;
+ switch(curnode->symbol->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ {
+ symlist_t *fields;
+ symbol_node_t *fieldnode;
+ int num_entries;
+
+ num_entries = 0;
+ fields = &curnode->symbol->info.rinfo->fields;
+ SLIST_FOREACH(fieldnode, fields, links) {
+ if (num_entries == 0)
+ aic_print_reg_dump_start(dfile,
+ curnode);
+ else
+ fputs(",\n", dfile);
+ num_entries++;
+ aic_print_reg_dump_entry(dfile, fieldnode);
}
- flag = R_NEXT;
+ aic_print_reg_dump_end(ofile, dfile,
+ curnode, num_entries);
+ }
+ default:
+ break;
}
+ }
- /* Put in the masks and bits */
- while (SLIST_FIRST(&masks) != NULL) {
- symbol_node_t *curnode;
- symbol_node_t *regnode;
- char *regname;
+ /* Fold in the masks and bits */
+ while (SLIST_FIRST(&masks) != NULL) {
+ char *regname;
- curnode = SLIST_FIRST(&masks);
- SLIST_REMOVE_HEAD(&masks, links);
+ 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);
- }
+ regnode = SLIST_FIRST(&curnode->symbol->info.finfo->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;
+ /* Add the aliases */
+ while (SLIST_FIRST(&aliases) != NULL) {
+ char *regname;
- curnode = SLIST_FIRST(&aliases);
- SLIST_REMOVE_HEAD(&aliases, links);
+ 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);
- }
+ 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_int8_t 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);
+ /* Output generated #defines. */
+ 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 FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ value = curnode->symbol->info.finfo->value;
+ 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, "\n\n");
+ 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;
+ 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);
- }
+ 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");
+
+ fprintf(ofile, "\n\n/* Downloaded Constant Definitions */\n");
+
+ for (i = 0; SLIST_FIRST(&download_constants) != NULL; i++) {
+ symbol_node_t *curnode;
- while (SLIST_FIRST(&download_constants) != NULL) {
- 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);
- 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, "\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/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
index 45b936bd32f9..afc22e8b4903 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
@@ -2,6 +2,7 @@
* Aic7xxx SCSI host adapter firmware asssembler symbol table definitions
*
* Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2002 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -36,9 +37,9 @@
* 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#6 $
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.h#17 $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_symbol.h,v 1.11 2000/09/22 22:19:55 gibbs Exp $
+ * $FreeBSD$
*/
#ifdef __linux__
@@ -53,13 +54,16 @@ typedef enum {
ALIAS,
SCBLOC,
SRAMLOC,
+ ENUM_ENTRY,
+ FIELD,
MASK,
- BIT,
+ ENUM,
CONST,
DOWNLOAD_CONST,
LABEL,
- CONDITIONAL
-}symtype;
+ CONDITIONAL,
+ MACRO
+} symtype;
typedef enum {
RO = 0x01,
@@ -67,24 +71,27 @@ typedef enum {
RW = 0x03
}amode_t;
+typedef SLIST_HEAD(symlist, symbol_node) symlist_t;
+
struct reg_info {
- u_int8_t address;
- int size;
- amode_t mode;
- u_int8_t valid_bitmask;
- int typecheck_masks;
+ u_int address;
+ int size;
+ amode_t mode;
+ symlist_t fields;
+ uint8_t valid_bitmask;
+ uint8_t modes;
+ int typecheck_masks;
};
-typedef SLIST_HEAD(symlist, symbol_node) symlist_t;
-
-struct mask_info {
+struct field_info {
symlist_t symrefs;
- u_int8_t mask;
+ uint8_t value;
+ uint8_t mask;
};
struct const_info {
- u_int8_t value;
- int define;
+ u_int value;
+ int define;
};
struct alias_info {
@@ -93,12 +100,26 @@ struct alias_info {
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;
@@ -108,12 +129,13 @@ typedef struct symbol {
char *name;
symtype type;
union {
- struct reg_info *rinfo;
- struct mask_info *minfo;
+ struct reg_info *rinfo;
+ struct field_info *finfo;
struct const_info *cinfo;
struct alias_info *ainfo;
struct label_info *linfo;
- struct cond_info *condinfo;
+ struct cond_info *condinfo;
+ struct macro_info *macroinfo;
}info;
} symbol_t;
@@ -161,25 +183,25 @@ TAILQ_HEAD(cs_tailq, critical_section);
SLIST_HEAD(scope_list, scope);
TAILQ_HEAD(scope_tailq, scope);
-void symbol_delete __P((symbol_t *symbol));
+void symbol_delete(symbol_t *symbol);
-void symtable_open __P((void));
+void symtable_open(void);
-void symtable_close __P((void));
+void symtable_close(void);
symbol_t *
- symtable_get __P((char *name));
+ symtable_get(char *name);
symbol_node_t *
- symlist_search __P((symlist_t *symlist, char *symname));
+ symlist_search(symlist_t *symlist, char *symname);
void
- symlist_add __P((symlist_t *symlist, symbol_t *symbol, int how));
+ symlist_add(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_free(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));
+void symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
+ symlist_t *symlist_src2);
+void symtable_dump(FILE *ofile, FILE *dfile);
diff --git a/drivers/scsi/aic7xxx/aiclib.c b/drivers/scsi/aic7xxx/aiclib.c
new file mode 100644
index 000000000000..50270d5483fd
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aiclib.c
@@ -0,0 +1,1226 @@
+/*
+ * Implementation of Utility functions for all SCSI device types.
+ *
+ * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs.
+ * Copyright (c) 1997, 1998 Kenneth D. Merry.
+ * 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, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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.
+ *
+ * $FreeBSD: src/sys/cam/scsi/scsi_all.c,v 1.38 2002/09/23 04:56:35 mjacob Exp $
+ * $Id$
+ */
+
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+
+/* Core SCSI definitions */
+#include "scsi.h"
+#include "hosts.h"
+#include "aiclib.h"
+#include "cam.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* FALSE */
+#ifndef TRUE
+#define TRUE 1
+#endif /* TRUE */
+#ifndef ERESTART
+#define ERESTART -1 /* restart syscall */
+#endif
+#ifndef EJUSTRETURN
+#define EJUSTRETURN -2 /* don't modify regs, just return */
+#endif
+
+static int ascentrycomp(const void *key, const void *member);
+static int senseentrycomp(const void *key, const void *member);
+static void fetchtableentries(int sense_key, int asc, int ascq,
+ struct scsi_inquiry_data *,
+ const struct sense_key_table_entry **,
+ const struct asc_table_entry **);
+static void * scsibsearch(const void *key, const void *base, size_t nmemb,
+ size_t size,
+ int (*compar)(const void *, const void *));
+typedef int (cam_quirkmatch_t)(caddr_t, caddr_t);
+static int cam_strmatch(const u_int8_t *str, const u_int8_t *pattern,
+ int str_len);
+static caddr_t cam_quirkmatch(caddr_t target, caddr_t quirk_table,
+ int num_entries, int entry_size,
+ cam_quirkmatch_t *comp_func);
+
+#define SCSI_NO_SENSE_STRINGS 1
+#if !defined(SCSI_NO_SENSE_STRINGS)
+#define SST(asc, ascq, action, desc) \
+ asc, ascq, action, desc
+#else
+static const char empty_string[] = "";
+
+#define SST(asc, ascq, action, desc) \
+ asc, ascq, action, empty_string
+#endif
+
+static const struct sense_key_table_entry sense_key_table[] =
+{
+ { SSD_KEY_NO_SENSE, SS_NOP, "NO SENSE" },
+ { SSD_KEY_RECOVERED_ERROR, SS_NOP|SSQ_PRINT_SENSE, "RECOVERED ERROR" },
+ {
+ SSD_KEY_NOT_READY, SS_TUR|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY,
+ "NOT READY"
+ },
+ { SSD_KEY_MEDIUM_ERROR, SS_RDEF, "MEDIUM ERROR" },
+ { SSD_KEY_HARDWARE_ERROR, SS_RDEF, "HARDWARE FAILURE" },
+ { SSD_KEY_ILLEGAL_REQUEST, SS_FATAL|EINVAL, "ILLEGAL REQUEST" },
+ { SSD_KEY_UNIT_ATTENTION, SS_FATAL|ENXIO, "UNIT ATTENTION" },
+ { SSD_KEY_DATA_PROTECT, SS_FATAL|EACCES, "DATA PROTECT" },
+ { SSD_KEY_BLANK_CHECK, SS_FATAL|ENOSPC, "BLANK CHECK" },
+ { SSD_KEY_Vendor_Specific, SS_FATAL|EIO, "Vendor Specific" },
+ { SSD_KEY_COPY_ABORTED, SS_FATAL|EIO, "COPY ABORTED" },
+ { SSD_KEY_ABORTED_COMMAND, SS_RDEF, "ABORTED COMMAND" },
+ { SSD_KEY_EQUAL, SS_NOP, "EQUAL" },
+ { SSD_KEY_VOLUME_OVERFLOW, SS_FATAL|EIO, "VOLUME OVERFLOW" },
+ { SSD_KEY_MISCOMPARE, SS_NOP, "MISCOMPARE" },
+ { SSD_KEY_RESERVED, SS_FATAL|EIO, "RESERVED" }
+};
+
+static const int sense_key_table_size =
+ sizeof(sense_key_table)/sizeof(sense_key_table[0]);
+
+static struct asc_table_entry quantum_fireball_entries[] = {
+ {SST(0x04, 0x0b, SS_START|SSQ_DECREMENT_COUNT|ENXIO,
+ "Logical unit not ready, initializing cmd. required")}
+};
+
+static struct asc_table_entry sony_mo_entries[] = {
+ {SST(0x04, 0x00, SS_START|SSQ_DECREMENT_COUNT|ENXIO,
+ "Logical unit not ready, cause not reportable")}
+};
+
+static struct scsi_sense_quirk_entry sense_quirk_table[] = {
+ {
+ /*
+ * The Quantum Fireball ST and SE like to return 0x04 0x0b when
+ * they really should return 0x04 0x02. 0x04,0x0b isn't
+ * defined in any SCSI spec, and it isn't mentioned in the
+ * hardware manual for these drives.
+ */
+ {T_DIRECT, SIP_MEDIA_FIXED, "QUANTUM", "FIREBALL S*", "*"},
+ /*num_sense_keys*/0,
+ sizeof(quantum_fireball_entries)/sizeof(struct asc_table_entry),
+ /*sense key entries*/NULL,
+ quantum_fireball_entries
+ },
+ {
+ /*
+ * This Sony MO drive likes to return 0x04, 0x00 when it
+ * isn't spun up.
+ */
+ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SONY", "SMO-*", "*"},
+ /*num_sense_keys*/0,
+ sizeof(sony_mo_entries)/sizeof(struct asc_table_entry),
+ /*sense key entries*/NULL,
+ sony_mo_entries
+ }
+};
+
+static const int sense_quirk_table_size =
+ sizeof(sense_quirk_table)/sizeof(sense_quirk_table[0]);
+
+static struct asc_table_entry asc_table[] = {
+/*
+ * From File: ASC-NUM.TXT
+ * SCSI ASC/ASCQ Assignments
+ * Numeric Sorted Listing
+ * as of 5/12/97
+ *
+ * D - DIRECT ACCESS DEVICE (SBC) device column key
+ * .T - SEQUENTIAL ACCESS DEVICE (SSC) -------------------
+ * . L - PRINTER DEVICE (SSC) blank = reserved
+ * . P - PROCESSOR DEVICE (SPC) not blank = allowed
+ * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC)
+ * . . R - CD DEVICE (MMC)
+ * . . S - SCANNER DEVICE (SGC)
+ * . . .O - OPTICAL MEMORY DEVICE (SBC)
+ * . . . M - MEDIA CHANGER DEVICE (SMC)
+ * . . . C - COMMUNICATION DEVICE (SSC)
+ * . . . .A - STORAGE ARRAY DEVICE (SCC)
+ * . . . . E - ENCLOSURE SERVICES DEVICE (SES)
+ * DTLPWRSOMCAE ASC ASCQ Action Description
+ * ------------ ---- ---- ------ -----------------------------------*/
+/* DTLPWRSOMCAE */{SST(0x00, 0x00, SS_NOP,
+ "No additional sense information") },
+/* T S */{SST(0x00, 0x01, SS_RDEF,
+ "Filemark detected") },
+/* T S */{SST(0x00, 0x02, SS_RDEF,
+ "End-of-partition/medium detected") },
+/* T */{SST(0x00, 0x03, SS_RDEF,
+ "Setmark detected") },
+/* T S */{SST(0x00, 0x04, SS_RDEF,
+ "Beginning-of-partition/medium detected") },
+/* T S */{SST(0x00, 0x05, SS_RDEF,
+ "End-of-data detected") },
+/* DTLPWRSOMCAE */{SST(0x00, 0x06, SS_RDEF,
+ "I/O process terminated") },
+/* R */{SST(0x00, 0x11, SS_FATAL|EBUSY,
+ "Audio play operation in progress") },
+/* R */{SST(0x00, 0x12, SS_NOP,
+ "Audio play operation paused") },
+/* R */{SST(0x00, 0x13, SS_NOP,
+ "Audio play operation successfully completed") },
+/* R */{SST(0x00, 0x14, SS_RDEF,
+ "Audio play operation stopped due to error") },
+/* R */{SST(0x00, 0x15, SS_NOP,
+ "No current audio status to return") },
+/* DTLPWRSOMCAE */{SST(0x00, 0x16, SS_FATAL|EBUSY,
+ "Operation in progress") },
+/* DTL WRSOM AE */{SST(0x00, 0x17, SS_RDEF,
+ "Cleaning requested") },
+/* D W O */{SST(0x01, 0x00, SS_RDEF,
+ "No index/sector signal") },
+/* D WR OM */{SST(0x02, 0x00, SS_RDEF,
+ "No seek complete") },
+/* DTL W SO */{SST(0x03, 0x00, SS_RDEF,
+ "Peripheral device write fault") },
+/* T */{SST(0x03, 0x01, SS_RDEF,
+ "No write current") },
+/* T */{SST(0x03, 0x02, SS_RDEF,
+ "Excessive write errors") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x00,
+ SS_TUR|SSQ_DELAY|SSQ_MANY|SSQ_DECREMENT_COUNT|EIO,
+ "Logical unit not ready, cause not reportable") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x01,
+ SS_TUR|SSQ_DELAY|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY,
+ "Logical unit is in process of becoming ready") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x02, SS_START|SSQ_DECREMENT_COUNT|ENXIO,
+ "Logical unit not ready, initializing cmd. required") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x03, SS_FATAL|ENXIO,
+ "Logical unit not ready, manual intervention required")},
+/* DTL O */{SST(0x04, 0x04, SS_FATAL|EBUSY,
+ "Logical unit not ready, format in progress") },
+/* DT W OMCA */{SST(0x04, 0x05, SS_FATAL|EBUSY,
+ "Logical unit not ready, rebuild in progress") },
+/* DT W OMCA */{SST(0x04, 0x06, SS_FATAL|EBUSY,
+ "Logical unit not ready, recalculation in progress") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x07, SS_FATAL|EBUSY,
+ "Logical unit not ready, operation in progress") },
+/* R */{SST(0x04, 0x08, SS_FATAL|EBUSY,
+ "Logical unit not ready, long write in progress") },
+/* DTL WRSOMCAE */{SST(0x05, 0x00, SS_RDEF,
+ "Logical unit does not respond to selection") },
+/* D WR OM */{SST(0x06, 0x00, SS_RDEF,
+ "No reference position found") },
+/* DTL WRSOM */{SST(0x07, 0x00, SS_RDEF,
+ "Multiple peripheral devices selected") },
+/* DTL WRSOMCAE */{SST(0x08, 0x00, SS_RDEF,
+ "Logical unit communication failure") },
+/* DTL WRSOMCAE */{SST(0x08, 0x01, SS_RDEF,
+ "Logical unit communication time-out") },
+/* DTL WRSOMCAE */{SST(0x08, 0x02, SS_RDEF,
+ "Logical unit communication parity error") },
+/* DT R OM */{SST(0x08, 0x03, SS_RDEF,
+ "Logical unit communication crc error (ultra-dma/32)")},
+/* DT WR O */{SST(0x09, 0x00, SS_RDEF,
+ "Track following error") },
+/* WR O */{SST(0x09, 0x01, SS_RDEF,
+ "Tracking servo failure") },
+/* WR O */{SST(0x09, 0x02, SS_RDEF,
+ "Focus servo failure") },
+/* WR O */{SST(0x09, 0x03, SS_RDEF,
+ "Spindle servo failure") },
+/* DT WR O */{SST(0x09, 0x04, SS_RDEF,
+ "Head select fault") },
+/* DTLPWRSOMCAE */{SST(0x0A, 0x00, SS_FATAL|ENOSPC,
+ "Error log overflow") },
+/* DTLPWRSOMCAE */{SST(0x0B, 0x00, SS_RDEF,
+ "Warning") },
+/* DTLPWRSOMCAE */{SST(0x0B, 0x01, SS_RDEF,
+ "Specified temperature exceeded") },
+/* DTLPWRSOMCAE */{SST(0x0B, 0x02, SS_RDEF,
+ "Enclosure degraded") },
+/* T RS */{SST(0x0C, 0x00, SS_RDEF,
+ "Write error") },
+/* D W O */{SST(0x0C, 0x01, SS_NOP|SSQ_PRINT_SENSE,
+ "Write error - recovered with auto reallocation") },
+/* D W O */{SST(0x0C, 0x02, SS_RDEF,
+ "Write error - auto reallocation failed") },
+/* D W O */{SST(0x0C, 0x03, SS_RDEF,
+ "Write error - recommend reassignment") },
+/* DT W O */{SST(0x0C, 0x04, SS_RDEF,
+ "Compression check miscompare error") },
+/* DT W O */{SST(0x0C, 0x05, SS_RDEF,
+ "Data expansion occurred during compression") },
+/* DT W O */{SST(0x0C, 0x06, SS_RDEF,
+ "Block not compressible") },
+/* R */{SST(0x0C, 0x07, SS_RDEF,
+ "Write error - recovery needed") },
+/* R */{SST(0x0C, 0x08, SS_RDEF,
+ "Write error - recovery failed") },
+/* R */{SST(0x0C, 0x09, SS_RDEF,
+ "Write error - loss of streaming") },
+/* R */{SST(0x0C, 0x0A, SS_RDEF,
+ "Write error - padding blocks added") },
+/* D W O */{SST(0x10, 0x00, SS_RDEF,
+ "ID CRC or ECC error") },
+/* DT WRSO */{SST(0x11, 0x00, SS_RDEF,
+ "Unrecovered read error") },
+/* DT W SO */{SST(0x11, 0x01, SS_RDEF,
+ "Read retries exhausted") },
+/* DT W SO */{SST(0x11, 0x02, SS_RDEF,
+ "Error too long to correct") },
+/* DT W SO */{SST(0x11, 0x03, SS_RDEF,
+ "Multiple read errors") },
+/* D W O */{SST(0x11, 0x04, SS_RDEF,
+ "Unrecovered read error - auto reallocate failed") },
+/* WR O */{SST(0x11, 0x05, SS_RDEF,
+ "L-EC uncorrectable error") },
+/* WR O */{SST(0x11, 0x06, SS_RDEF,
+ "CIRC unrecovered error") },
+/* W O */{SST(0x11, 0x07, SS_RDEF,
+ "Data re-synchronization error") },
+/* T */{SST(0x11, 0x08, SS_RDEF,
+ "Incomplete block read") },
+/* T */{SST(0x11, 0x09, SS_RDEF,
+ "No gap found") },
+/* DT O */{SST(0x11, 0x0A, SS_RDEF,
+ "Miscorrected error") },
+/* D W O */{SST(0x11, 0x0B, SS_RDEF,
+ "Unrecovered read error - recommend reassignment") },
+/* D W O */{SST(0x11, 0x0C, SS_RDEF,
+ "Unrecovered read error - recommend rewrite the data")},
+/* DT WR O */{SST(0x11, 0x0D, SS_RDEF,
+ "De-compression CRC error") },
+/* DT WR O */{SST(0x11, 0x0E, SS_RDEF,
+ "Cannot decompress using declared algorithm") },
+/* R */{SST(0x11, 0x0F, SS_RDEF,
+ "Error reading UPC/EAN number") },
+/* R */{SST(0x11, 0x10, SS_RDEF,
+ "Error reading ISRC number") },
+/* R */{SST(0x11, 0x11, SS_RDEF,
+ "Read error - loss of streaming") },
+/* D W O */{SST(0x12, 0x00, SS_RDEF,
+ "Address mark not found for id field") },
+/* D W O */{SST(0x13, 0x00, SS_RDEF,
+ "Address mark not found for data field") },
+/* DTL WRSO */{SST(0x14, 0x00, SS_RDEF,
+ "Recorded entity not found") },
+/* DT WR O */{SST(0x14, 0x01, SS_RDEF,
+ "Record not found") },
+/* T */{SST(0x14, 0x02, SS_RDEF,
+ "Filemark or setmark not found") },
+/* T */{SST(0x14, 0x03, SS_RDEF,
+ "End-of-data not found") },
+/* T */{SST(0x14, 0x04, SS_RDEF,
+ "Block sequence error") },
+/* DT W O */{SST(0x14, 0x05, SS_RDEF,
+ "Record not found - recommend reassignment") },
+/* DT W O */{SST(0x14, 0x06, SS_RDEF,
+ "Record not found - data auto-reallocated") },
+/* DTL WRSOM */{SST(0x15, 0x00, SS_RDEF,
+ "Random positioning error") },
+/* DTL WRSOM */{SST(0x15, 0x01, SS_RDEF,
+ "Mechanical positioning error") },
+/* DT WR O */{SST(0x15, 0x02, SS_RDEF,
+ "Positioning error detected by read of medium") },
+/* D W O */{SST(0x16, 0x00, SS_RDEF,
+ "Data synchronization mark error") },
+/* D W O */{SST(0x16, 0x01, SS_RDEF,
+ "Data sync error - data rewritten") },
+/* D W O */{SST(0x16, 0x02, SS_RDEF,
+ "Data sync error - recommend rewrite") },
+/* D W O */{SST(0x16, 0x03, SS_NOP|SSQ_PRINT_SENSE,
+ "Data sync error - data auto-reallocated") },
+/* D W O */{SST(0x16, 0x04, SS_RDEF,
+ "Data sync error - recommend reassignment") },
+/* DT WRSO */{SST(0x17, 0x00, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with no error correction applied") },
+/* DT WRSO */{SST(0x17, 0x01, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with retries") },
+/* DT WR O */{SST(0x17, 0x02, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with positive head offset") },
+/* DT WR O */{SST(0x17, 0x03, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with negative head offset") },
+/* WR O */{SST(0x17, 0x04, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with retries and/or CIRC applied") },
+/* D WR O */{SST(0x17, 0x05, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data using previous sector id") },
+/* D W O */{SST(0x17, 0x06, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data without ECC - data auto-reallocated") },
+/* D W O */{SST(0x17, 0x07, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data without ECC - recommend reassignment")},
+/* D W O */{SST(0x17, 0x08, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data without ECC - recommend rewrite") },
+/* D W O */{SST(0x17, 0x09, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data without ECC - data rewritten") },
+/* D W O */{SST(0x18, 0x00, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with error correction applied") },
+/* D WR O */{SST(0x18, 0x01, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with error corr. & retries applied") },
+/* D WR O */{SST(0x18, 0x02, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data - data auto-reallocated") },
+/* R */{SST(0x18, 0x03, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with CIRC") },
+/* R */{SST(0x18, 0x04, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with L-EC") },
+/* D WR O */{SST(0x18, 0x05, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data - recommend reassignment") },
+/* D WR O */{SST(0x18, 0x06, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data - recommend rewrite") },
+/* D W O */{SST(0x18, 0x07, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with ECC - data rewritten") },
+/* D O */{SST(0x19, 0x00, SS_RDEF,
+ "Defect list error") },
+/* D O */{SST(0x19, 0x01, SS_RDEF,
+ "Defect list not available") },
+/* D O */{SST(0x19, 0x02, SS_RDEF,
+ "Defect list error in primary list") },
+/* D O */{SST(0x19, 0x03, SS_RDEF,
+ "Defect list error in grown list") },
+/* DTLPWRSOMCAE */{SST(0x1A, 0x00, SS_RDEF,
+ "Parameter list length error") },
+/* DTLPWRSOMCAE */{SST(0x1B, 0x00, SS_RDEF,
+ "Synchronous data transfer error") },
+/* D O */{SST(0x1C, 0x00, SS_RDEF,
+ "Defect list not found") },
+/* D O */{SST(0x1C, 0x01, SS_RDEF,
+ "Primary defect list not found") },
+/* D O */{SST(0x1C, 0x02, SS_RDEF,
+ "Grown defect list not found") },
+/* D W O */{SST(0x1D, 0x00, SS_FATAL,
+ "Miscompare during verify operation" )},
+/* D W O */{SST(0x1E, 0x00, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered id with ecc correction") },
+/* D O */{SST(0x1F, 0x00, SS_RDEF,
+ "Partial defect list transfer") },
+/* DTLPWRSOMCAE */{SST(0x20, 0x00, SS_FATAL|EINVAL,
+ "Invalid command operation code") },
+/* DT WR OM */{SST(0x21, 0x00, SS_FATAL|EINVAL,
+ "Logical block address out of range" )},
+/* DT WR OM */{SST(0x21, 0x01, SS_FATAL|EINVAL,
+ "Invalid element address") },
+/* D */{SST(0x22, 0x00, SS_FATAL|EINVAL,
+ "Illegal function") }, /* Deprecated. Use 20 00, 24 00, or 26 00 instead */
+/* DTLPWRSOMCAE */{SST(0x24, 0x00, SS_FATAL|EINVAL,
+ "Invalid field in CDB") },
+/* DTLPWRSOMCAE */{SST(0x25, 0x00, SS_FATAL|ENXIO,
+ "Logical unit not supported") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x00, SS_FATAL|EINVAL,
+ "Invalid field in parameter list") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x01, SS_FATAL|EINVAL,
+ "Parameter not supported") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x02, SS_FATAL|EINVAL,
+ "Parameter value invalid") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x03, SS_FATAL|EINVAL,
+ "Threshold parameters not supported") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x04, SS_FATAL|EINVAL,
+ "Invalid release of active persistent reservation") },
+/* DT W O */{SST(0x27, 0x00, SS_FATAL|EACCES,
+ "Write protected") },
+/* DT W O */{SST(0x27, 0x01, SS_FATAL|EACCES,
+ "Hardware write protected") },
+/* DT W O */{SST(0x27, 0x02, SS_FATAL|EACCES,
+ "Logical unit software write protected") },
+/* T */{SST(0x27, 0x03, SS_FATAL|EACCES,
+ "Associated write protect") },
+/* T */{SST(0x27, 0x04, SS_FATAL|EACCES,
+ "Persistent write protect") },
+/* T */{SST(0x27, 0x05, SS_FATAL|EACCES,
+ "Permanent write protect") },
+/* DTLPWRSOMCAE */{SST(0x28, 0x00, SS_RDEF,
+ "Not ready to ready change, medium may have changed") },
+/* DTLPWRSOMCAE */{SST(0x28, 0x01, SS_FATAL|ENXIO,
+ "Import or export element accessed") },
+/*
+ * XXX JGibbs - All of these should use the same errno, but I don't think
+ * ENXIO is the correct choice. Should we borrow from the networking
+ * errnos? ECONNRESET anyone?
+ */
+/* DTLPWRSOMCAE */{SST(0x29, 0x00, SS_RDEF,
+ "Power on, reset, or bus device reset occurred") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x01, SS_RDEF,
+ "Power on occurred") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x02, SS_RDEF,
+ "Scsi bus reset occurred") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x03, SS_RDEF,
+ "Bus device reset function occurred") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x04, SS_RDEF,
+ "Device internal reset") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x05, SS_RDEF,
+ "Transceiver mode changed to single-ended") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x06, SS_RDEF,
+ "Transceiver mode changed to LVD") },
+/* DTL WRSOMCAE */{SST(0x2A, 0x00, SS_RDEF,
+ "Parameters changed") },
+/* DTL WRSOMCAE */{SST(0x2A, 0x01, SS_RDEF,
+ "Mode parameters changed") },
+/* DTL WRSOMCAE */{SST(0x2A, 0x02, SS_RDEF,
+ "Log parameters changed") },
+/* DTLPWRSOMCAE */{SST(0x2A, 0x03, SS_RDEF,
+ "Reservations preempted") },
+/* DTLPWRSO C */{SST(0x2B, 0x00, SS_RDEF,
+ "Copy cannot execute since host cannot disconnect") },
+/* DTLPWRSOMCAE */{SST(0x2C, 0x00, SS_RDEF,
+ "Command sequence error") },
+/* S */{SST(0x2C, 0x01, SS_RDEF,
+ "Too many windows specified") },
+/* S */{SST(0x2C, 0x02, SS_RDEF,
+ "Invalid combination of windows specified") },
+/* R */{SST(0x2C, 0x03, SS_RDEF,
+ "Current program area is not empty") },
+/* R */{SST(0x2C, 0x04, SS_RDEF,
+ "Current program area is empty") },
+/* T */{SST(0x2D, 0x00, SS_RDEF,
+ "Overwrite error on update in place") },
+/* DTLPWRSOMCAE */{SST(0x2F, 0x00, SS_RDEF,
+ "Commands cleared by another initiator") },
+/* DT WR OM */{SST(0x30, 0x00, SS_RDEF,
+ "Incompatible medium installed") },
+/* DT WR O */{SST(0x30, 0x01, SS_RDEF,
+ "Cannot read medium - unknown format") },
+/* DT WR O */{SST(0x30, 0x02, SS_RDEF,
+ "Cannot read medium - incompatible format") },
+/* DT */{SST(0x30, 0x03, SS_RDEF,
+ "Cleaning cartridge installed") },
+/* DT WR O */{SST(0x30, 0x04, SS_RDEF,
+ "Cannot write medium - unknown format") },
+/* DT WR O */{SST(0x30, 0x05, SS_RDEF,
+ "Cannot write medium - incompatible format") },
+/* DT W O */{SST(0x30, 0x06, SS_RDEF,
+ "Cannot format medium - incompatible medium") },
+/* DTL WRSOM AE */{SST(0x30, 0x07, SS_RDEF,
+ "Cleaning failure") },
+/* R */{SST(0x30, 0x08, SS_RDEF,
+ "Cannot write - application code mismatch") },
+/* R */{SST(0x30, 0x09, SS_RDEF,
+ "Current session not fixated for append") },
+/* DT WR O */{SST(0x31, 0x00, SS_RDEF,
+ "Medium format corrupted") },
+/* D L R O */{SST(0x31, 0x01, SS_RDEF,
+ "Format command failed") },
+/* D W O */{SST(0x32, 0x00, SS_RDEF,
+ "No defect spare location available") },
+/* D W O */{SST(0x32, 0x01, SS_RDEF,
+ "Defect list update failure") },
+/* T */{SST(0x33, 0x00, SS_RDEF,
+ "Tape length error") },
+/* DTLPWRSOMCAE */{SST(0x34, 0x00, SS_RDEF,
+ "Enclosure failure") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x00, SS_RDEF,
+ "Enclosure services failure") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x01, SS_RDEF,
+ "Unsupported enclosure function") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x02, SS_RDEF,
+ "Enclosure services unavailable") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x03, SS_RDEF,
+ "Enclosure services transfer failure") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x04, SS_RDEF,
+ "Enclosure services transfer refused") },
+/* L */{SST(0x36, 0x00, SS_RDEF,
+ "Ribbon, ink, or toner failure") },
+/* DTL WRSOMCAE */{SST(0x37, 0x00, SS_RDEF,
+ "Rounded parameter") },
+/* DTL WRSOMCAE */{SST(0x39, 0x00, SS_RDEF,
+ "Saving parameters not supported") },
+/* DTL WRSOM */{SST(0x3A, 0x00, SS_FATAL|ENXIO,
+ "Medium not present") },
+/* DT WR OM */{SST(0x3A, 0x01, SS_FATAL|ENXIO,
+ "Medium not present - tray closed") },
+/* DT WR OM */{SST(0x3A, 0x02, SS_FATAL|ENXIO,
+ "Medium not present - tray open") },
+/* TL */{SST(0x3B, 0x00, SS_RDEF,
+ "Sequential positioning error") },
+/* T */{SST(0x3B, 0x01, SS_RDEF,
+ "Tape position error at beginning-of-medium") },
+/* T */{SST(0x3B, 0x02, SS_RDEF,
+ "Tape position error at end-of-medium") },
+/* L */{SST(0x3B, 0x03, SS_RDEF,
+ "Tape or electronic vertical forms unit not ready") },
+/* L */{SST(0x3B, 0x04, SS_RDEF,
+ "Slew failure") },
+/* L */{SST(0x3B, 0x05, SS_RDEF,
+ "Paper jam") },
+/* L */{SST(0x3B, 0x06, SS_RDEF,
+ "Failed to sense top-of-form") },
+/* L */{SST(0x3B, 0x07, SS_RDEF,
+ "Failed to sense bottom-of-form") },
+/* T */{SST(0x3B, 0x08, SS_RDEF,
+ "Reposition error") },
+/* S */{SST(0x3B, 0x09, SS_RDEF,
+ "Read past end of medium") },
+/* S */{SST(0x3B, 0x0A, SS_RDEF,
+ "Read past beginning of medium") },
+/* S */{SST(0x3B, 0x0B, SS_RDEF,
+ "Position past end of medium") },
+/* T S */{SST(0x3B, 0x0C, SS_RDEF,
+ "Position past beginning of medium") },
+/* DT WR OM */{SST(0x3B, 0x0D, SS_FATAL|ENOSPC,
+ "Medium destination element full") },
+/* DT WR OM */{SST(0x3B, 0x0E, SS_RDEF,
+ "Medium source element empty") },
+/* R */{SST(0x3B, 0x0F, SS_RDEF,
+ "End of medium reached") },
+/* DT WR OM */{SST(0x3B, 0x11, SS_RDEF,
+ "Medium magazine not accessible") },
+/* DT WR OM */{SST(0x3B, 0x12, SS_RDEF,
+ "Medium magazine removed") },
+/* DT WR OM */{SST(0x3B, 0x13, SS_RDEF,
+ "Medium magazine inserted") },
+/* DT WR OM */{SST(0x3B, 0x14, SS_RDEF,
+ "Medium magazine locked") },
+/* DT WR OM */{SST(0x3B, 0x15, SS_RDEF,
+ "Medium magazine unlocked") },
+/* DTLPWRSOMCAE */{SST(0x3D, 0x00, SS_RDEF,
+ "Invalid bits in identify message") },
+/* DTLPWRSOMCAE */{SST(0x3E, 0x00, SS_RDEF,
+ "Logical unit has not self-configured yet") },
+/* DTLPWRSOMCAE */{SST(0x3E, 0x01, SS_RDEF,
+ "Logical unit failure") },
+/* DTLPWRSOMCAE */{SST(0x3E, 0x02, SS_RDEF,
+ "Timeout on logical unit") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x00, SS_RDEF,
+ "Target operating conditions have changed") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x01, SS_RDEF,
+ "Microcode has been changed") },
+/* DTLPWRSOMC */{SST(0x3F, 0x02, SS_RDEF,
+ "Changed operating definition") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x03, SS_INQ_REFRESH|SSQ_DECREMENT_COUNT,
+ "Inquiry data has changed") },
+/* DT WR OMCAE */{SST(0x3F, 0x04, SS_RDEF,
+ "Component device attached") },
+/* DT WR OMCAE */{SST(0x3F, 0x05, SS_RDEF,
+ "Device identifier changed") },
+/* DT WR OMCAE */{SST(0x3F, 0x06, SS_RDEF,
+ "Redundancy group created or modified") },
+/* DT WR OMCAE */{SST(0x3F, 0x07, SS_RDEF,
+ "Redundancy group deleted") },
+/* DT WR OMCAE */{SST(0x3F, 0x08, SS_RDEF,
+ "Spare created or modified") },
+/* DT WR OMCAE */{SST(0x3F, 0x09, SS_RDEF,
+ "Spare deleted") },
+/* DT WR OMCAE */{SST(0x3F, 0x0A, SS_RDEF,
+ "Volume set created or modified") },
+/* DT WR OMCAE */{SST(0x3F, 0x0B, SS_RDEF,
+ "Volume set deleted") },
+/* DT WR OMCAE */{SST(0x3F, 0x0C, SS_RDEF,
+ "Volume set deassigned") },
+/* DT WR OMCAE */{SST(0x3F, 0x0D, SS_RDEF,
+ "Volume set reassigned") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x0E, SS_RDEF,
+ "Reported luns data has changed") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x0F, SS_RETRY|SSQ_DECREMENT_COUNT
+ | SSQ_DELAY_RANDOM|EBUSY,
+ "Echo buffer overwritten") },
+/* DT WR OM B*/{SST(0x3F, 0x0F, SS_RDEF, "Medium Loadable") },
+/* DT WR OM B*/{SST(0x3F, 0x0F, SS_RDEF,
+ "Medium auxiliary memory accessible") },
+/* D */{SST(0x40, 0x00, SS_RDEF,
+ "Ram failure") }, /* deprecated - use 40 NN instead */
+/* DTLPWRSOMCAE */{SST(0x40, 0x80, SS_RDEF,
+ "Diagnostic failure: ASCQ = Component ID") },
+/* DTLPWRSOMCAE */{SST(0x40, 0xFF, SS_RDEF|SSQ_RANGE,
+ NULL) },/* Range 0x80->0xFF */
+/* D */{SST(0x41, 0x00, SS_RDEF,
+ "Data path failure") }, /* deprecated - use 40 NN instead */
+/* D */{SST(0x42, 0x00, SS_RDEF,
+ "Power-on or self-test failure") }, /* deprecated - use 40 NN instead */
+/* DTLPWRSOMCAE */{SST(0x43, 0x00, SS_RDEF,
+ "Message error") },
+/* DTLPWRSOMCAE */{SST(0x44, 0x00, SS_RDEF,
+ "Internal target failure") },
+/* DTLPWRSOMCAE */{SST(0x45, 0x00, SS_RDEF,
+ "Select or reselect failure") },
+/* DTLPWRSOMC */{SST(0x46, 0x00, SS_RDEF,
+ "Unsuccessful soft reset") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x00, SS_RDEF|SSQ_FALLBACK,
+ "SCSI parity error") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x01, SS_RDEF|SSQ_FALLBACK,
+ "Data Phase CRC error detected") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x02, SS_RDEF|SSQ_FALLBACK,
+ "SCSI parity error detected during ST data phase") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x03, SS_RDEF|SSQ_FALLBACK,
+ "Information Unit iuCRC error") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x04, SS_RDEF|SSQ_FALLBACK,
+ "Asynchronous information protection error detected") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x05, SS_RDEF|SSQ_FALLBACK,
+ "Protocol server CRC error") },
+/* DTLPWRSOMCAE */{SST(0x48, 0x00, SS_RDEF|SSQ_FALLBACK,
+ "Initiator detected error message received") },
+/* DTLPWRSOMCAE */{SST(0x49, 0x00, SS_RDEF,
+ "Invalid message error") },
+/* DTLPWRSOMCAE */{SST(0x4A, 0x00, SS_RDEF,
+ "Command phase error") },
+/* DTLPWRSOMCAE */{SST(0x4B, 0x00, SS_RDEF,
+ "Data phase error") },
+/* DTLPWRSOMCAE */{SST(0x4C, 0x00, SS_RDEF,
+ "Logical unit failed self-configuration") },
+/* DTLPWRSOMCAE */{SST(0x4D, 0x00, SS_RDEF,
+ "Tagged overlapped commands: ASCQ = Queue tag ID") },
+/* DTLPWRSOMCAE */{SST(0x4D, 0xFF, SS_RDEF|SSQ_RANGE,
+ NULL)}, /* Range 0x00->0xFF */
+/* DTLPWRSOMCAE */{SST(0x4E, 0x00, SS_RDEF,
+ "Overlapped commands attempted") },
+/* T */{SST(0x50, 0x00, SS_RDEF,
+ "Write append error") },
+/* T */{SST(0x50, 0x01, SS_RDEF,
+ "Write append position error") },
+/* T */{SST(0x50, 0x02, SS_RDEF,
+ "Position error related to timing") },
+/* T O */{SST(0x51, 0x00, SS_RDEF,
+ "Erase failure") },
+/* T */{SST(0x52, 0x00, SS_RDEF,
+ "Cartridge fault") },
+/* DTL WRSOM */{SST(0x53, 0x00, SS_RDEF,
+ "Media load or eject failed") },
+/* T */{SST(0x53, 0x01, SS_RDEF,
+ "Unload tape failure") },
+/* DT WR OM */{SST(0x53, 0x02, SS_RDEF,
+ "Medium removal prevented") },
+/* P */{SST(0x54, 0x00, SS_RDEF,
+ "Scsi to host system interface failure") },
+/* P */{SST(0x55, 0x00, SS_RDEF,
+ "System resource failure") },
+/* D O */{SST(0x55, 0x01, SS_FATAL|ENOSPC,
+ "System buffer full") },
+/* R */{SST(0x57, 0x00, SS_RDEF,
+ "Unable to recover table-of-contents") },
+/* O */{SST(0x58, 0x00, SS_RDEF,
+ "Generation does not exist") },
+/* O */{SST(0x59, 0x00, SS_RDEF,
+ "Updated block read") },
+/* DTLPWRSOM */{SST(0x5A, 0x00, SS_RDEF,
+ "Operator request or state change input") },
+/* DT WR OM */{SST(0x5A, 0x01, SS_RDEF,
+ "Operator medium removal request") },
+/* DT W O */{SST(0x5A, 0x02, SS_RDEF,
+ "Operator selected write protect") },
+/* DT W O */{SST(0x5A, 0x03, SS_RDEF,
+ "Operator selected write permit") },
+/* DTLPWRSOM */{SST(0x5B, 0x00, SS_RDEF,
+ "Log exception") },
+/* DTLPWRSOM */{SST(0x5B, 0x01, SS_RDEF,
+ "Threshold condition met") },
+/* DTLPWRSOM */{SST(0x5B, 0x02, SS_RDEF,
+ "Log counter at maximum") },
+/* DTLPWRSOM */{SST(0x5B, 0x03, SS_RDEF,
+ "Log list codes exhausted") },
+/* D O */{SST(0x5C, 0x00, SS_RDEF,
+ "RPL status change") },
+/* D O */{SST(0x5C, 0x01, SS_NOP|SSQ_PRINT_SENSE,
+ "Spindles synchronized") },
+/* D O */{SST(0x5C, 0x02, SS_RDEF,
+ "Spindles not synchronized") },
+/* DTLPWRSOMCAE */{SST(0x5D, 0x00, SS_RDEF,
+ "Failure prediction threshold exceeded") },
+/* DTLPWRSOMCAE */{SST(0x5D, 0xFF, SS_RDEF,
+ "Failure prediction threshold exceeded (false)") },
+/* DTLPWRSO CA */{SST(0x5E, 0x00, SS_RDEF,
+ "Low power condition on") },
+/* DTLPWRSO CA */{SST(0x5E, 0x01, SS_RDEF,
+ "Idle condition activated by timer") },
+/* DTLPWRSO CA */{SST(0x5E, 0x02, SS_RDEF,
+ "Standby condition activated by timer") },
+/* DTLPWRSO CA */{SST(0x5E, 0x03, SS_RDEF,
+ "Idle condition activated by command") },
+/* DTLPWRSO CA */{SST(0x5E, 0x04, SS_RDEF,
+ "Standby condition activated by command") },
+/* S */{SST(0x60, 0x00, SS_RDEF,
+ "Lamp failure") },
+/* S */{SST(0x61, 0x00, SS_RDEF,
+ "Video acquisition error") },
+/* S */{SST(0x61, 0x01, SS_RDEF,
+ "Unable to acquire video") },
+/* S */{SST(0x61, 0x02, SS_RDEF,
+ "Out of focus") },
+/* S */{SST(0x62, 0x00, SS_RDEF,
+ "Scan head positioning error") },
+/* R */{SST(0x63, 0x00, SS_RDEF,
+ "End of user area encountered on this track") },
+/* R */{SST(0x63, 0x01, SS_FATAL|ENOSPC,
+ "Packet does not fit in available space") },
+/* R */{SST(0x64, 0x00, SS_RDEF,
+ "Illegal mode for this track") },
+/* R */{SST(0x64, 0x01, SS_RDEF,
+ "Invalid packet size") },
+/* DTLPWRSOMCAE */{SST(0x65, 0x00, SS_RDEF,
+ "Voltage fault") },
+/* S */{SST(0x66, 0x00, SS_RDEF,
+ "Automatic document feeder cover up") },
+/* S */{SST(0x66, 0x01, SS_RDEF,
+ "Automatic document feeder lift up") },
+/* S */{SST(0x66, 0x02, SS_RDEF,
+ "Document jam in automatic document feeder") },
+/* S */{SST(0x66, 0x03, SS_RDEF,
+ "Document miss feed automatic in document feeder") },
+/* A */{SST(0x67, 0x00, SS_RDEF,
+ "Configuration failure") },
+/* A */{SST(0x67, 0x01, SS_RDEF,
+ "Configuration of incapable logical units failed") },
+/* A */{SST(0x67, 0x02, SS_RDEF,
+ "Add logical unit failed") },
+/* A */{SST(0x67, 0x03, SS_RDEF,
+ "Modification of logical unit failed") },
+/* A */{SST(0x67, 0x04, SS_RDEF,
+ "Exchange of logical unit failed") },
+/* A */{SST(0x67, 0x05, SS_RDEF,
+ "Remove of logical unit failed") },
+/* A */{SST(0x67, 0x06, SS_RDEF,
+ "Attachment of logical unit failed") },
+/* A */{SST(0x67, 0x07, SS_RDEF,
+ "Creation of logical unit failed") },
+/* A */{SST(0x68, 0x00, SS_RDEF,
+ "Logical unit not configured") },
+/* A */{SST(0x69, 0x00, SS_RDEF,
+ "Data loss on logical unit") },
+/* A */{SST(0x69, 0x01, SS_RDEF,
+ "Multiple logical unit failures") },
+/* A */{SST(0x69, 0x02, SS_RDEF,
+ "Parity/data mismatch") },
+/* A */{SST(0x6A, 0x00, SS_RDEF,
+ "Informational, refer to log") },
+/* A */{SST(0x6B, 0x00, SS_RDEF,
+ "State change has occurred") },
+/* A */{SST(0x6B, 0x01, SS_RDEF,
+ "Redundancy level got better") },
+/* A */{SST(0x6B, 0x02, SS_RDEF,
+ "Redundancy level got worse") },
+/* A */{SST(0x6C, 0x00, SS_RDEF,
+ "Rebuild failure occurred") },
+/* A */{SST(0x6D, 0x00, SS_RDEF,
+ "Recalculate failure occurred") },
+/* A */{SST(0x6E, 0x00, SS_RDEF,
+ "Command to logical unit failed") },
+/* T */{SST(0x70, 0x00, SS_RDEF,
+ "Decompression exception short: ASCQ = Algorithm ID") },
+/* T */{SST(0x70, 0xFF, SS_RDEF|SSQ_RANGE,
+ NULL) }, /* Range 0x00 -> 0xFF */
+/* T */{SST(0x71, 0x00, SS_RDEF,
+ "Decompression exception long: ASCQ = Algorithm ID") },
+/* T */{SST(0x71, 0xFF, SS_RDEF|SSQ_RANGE,
+ NULL) }, /* Range 0x00 -> 0xFF */
+/* R */{SST(0x72, 0x00, SS_RDEF,
+ "Session fixation error") },
+/* R */{SST(0x72, 0x01, SS_RDEF,
+ "Session fixation error writing lead-in") },
+/* R */{SST(0x72, 0x02, SS_RDEF,
+ "Session fixation error writing lead-out") },
+/* R */{SST(0x72, 0x03, SS_RDEF,
+ "Session fixation error - incomplete track in session") },
+/* R */{SST(0x72, 0x04, SS_RDEF,
+ "Empty or partially written reserved track") },
+/* R */{SST(0x73, 0x00, SS_RDEF,
+ "CD control error") },
+/* R */{SST(0x73, 0x01, SS_RDEF,
+ "Power calibration area almost full") },
+/* R */{SST(0x73, 0x02, SS_FATAL|ENOSPC,
+ "Power calibration area is full") },
+/* R */{SST(0x73, 0x03, SS_RDEF,
+ "Power calibration area error") },
+/* R */{SST(0x73, 0x04, SS_RDEF,
+ "Program memory area update failure") },
+/* R */{SST(0x73, 0x05, SS_RDEF,
+ "program memory area is full") }
+};
+
+static const int asc_table_size = sizeof(asc_table)/sizeof(asc_table[0]);
+
+struct asc_key
+{
+ int asc;
+ int ascq;
+};
+
+static int
+ascentrycomp(const void *key, const void *member)
+{
+ int asc;
+ int ascq;
+ const struct asc_table_entry *table_entry;
+
+ asc = ((const struct asc_key *)key)->asc;
+ ascq = ((const struct asc_key *)key)->ascq;
+ table_entry = (const struct asc_table_entry *)member;
+
+ if (asc >= table_entry->asc) {
+
+ if (asc > table_entry->asc)
+ return (1);
+
+ if (ascq <= table_entry->ascq) {
+ /* Check for ranges */
+ if (ascq == table_entry->ascq
+ || ((table_entry->action & SSQ_RANGE) != 0
+ && ascq >= (table_entry - 1)->ascq))
+ return (0);
+ return (-1);
+ }
+ return (1);
+ }
+ return (-1);
+}
+
+static int
+senseentrycomp(const void *key, const void *member)
+{
+ int sense_key;
+ const struct sense_key_table_entry *table_entry;
+
+ sense_key = *((const int *)key);
+ table_entry = (const struct sense_key_table_entry *)member;
+
+ if (sense_key >= table_entry->sense_key) {
+ if (sense_key == table_entry->sense_key)
+ return (0);
+ return (1);
+ }
+ return (-1);
+}
+
+static void
+fetchtableentries(int sense_key, int asc, int ascq,
+ struct scsi_inquiry_data *inq_data,
+ const struct sense_key_table_entry **sense_entry,
+ const struct asc_table_entry **asc_entry)
+{
+ void *match;
+ const struct asc_table_entry *asc_tables[2];
+ const struct sense_key_table_entry *sense_tables[2];
+ struct asc_key asc_ascq;
+ size_t asc_tables_size[2];
+ size_t sense_tables_size[2];
+ int num_asc_tables;
+ int num_sense_tables;
+ int i;
+
+ /* Default to failure */
+ *sense_entry = NULL;
+ *asc_entry = NULL;
+ match = NULL;
+ if (inq_data != NULL)
+ match = cam_quirkmatch((void *)inq_data,
+ (void *)sense_quirk_table,
+ sense_quirk_table_size,
+ sizeof(*sense_quirk_table),
+ aic_inquiry_match);
+
+ if (match != NULL) {
+ struct scsi_sense_quirk_entry *quirk;
+
+ quirk = (struct scsi_sense_quirk_entry *)match;
+ asc_tables[0] = quirk->asc_info;
+ asc_tables_size[0] = quirk->num_ascs;
+ asc_tables[1] = asc_table;
+ asc_tables_size[1] = asc_table_size;
+ num_asc_tables = 2;
+ sense_tables[0] = quirk->sense_key_info;
+ sense_tables_size[0] = quirk->num_sense_keys;
+ sense_tables[1] = sense_key_table;
+ sense_tables_size[1] = sense_key_table_size;
+ num_sense_tables = 2;
+ } else {
+ asc_tables[0] = asc_table;
+ asc_tables_size[0] = asc_table_size;
+ num_asc_tables = 1;
+ sense_tables[0] = sense_key_table;
+ sense_tables_size[0] = sense_key_table_size;
+ num_sense_tables = 1;
+ }
+
+ asc_ascq.asc = asc;
+ asc_ascq.ascq = ascq;
+ for (i = 0; i < num_asc_tables; i++) {
+ void *found_entry;
+
+ found_entry = scsibsearch(&asc_ascq, asc_tables[i],
+ asc_tables_size[i],
+ sizeof(**asc_tables),
+ ascentrycomp);
+
+ if (found_entry) {
+ *asc_entry = (struct asc_table_entry *)found_entry;
+ break;
+ }
+ }
+
+ for (i = 0; i < num_sense_tables; i++) {
+ void *found_entry;
+
+ found_entry = scsibsearch(&sense_key, sense_tables[i],
+ sense_tables_size[i],
+ sizeof(**sense_tables),
+ senseentrycomp);
+
+ if (found_entry) {
+ *sense_entry =
+ (struct sense_key_table_entry *)found_entry;
+ break;
+ }
+ }
+}
+
+static void *
+scsibsearch(const void *key, const void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *))
+{
+ const void *entry;
+ u_int l;
+ u_int u;
+ u_int m;
+
+ l = -1;
+ u = nmemb;
+ while (l + 1 != u) {
+ m = (l + u) / 2;
+ entry = base + m * size;
+ if (compar(key, entry) > 0)
+ l = m;
+ else
+ u = m;
+ }
+
+ entry = base + u * size;
+ if (u == nmemb
+ || compar(key, entry) != 0)
+ return (NULL);
+
+ return ((void *)entry);
+}
+
+/*
+ * Compare string with pattern, returning 0 on match.
+ * Short pattern matches trailing blanks in name,
+ * wildcard '*' in pattern matches rest of name,
+ * wildcard '?' matches a single non-space character.
+ */
+static int
+cam_strmatch(const uint8_t *str, const uint8_t *pattern, int str_len)
+{
+
+ while (*pattern != '\0'&& str_len > 0) {
+
+ if (*pattern == '*') {
+ return (0);
+ }
+ if ((*pattern != *str)
+ && (*pattern != '?' || *str == ' ')) {
+ return (1);
+ }
+ pattern++;
+ str++;
+ str_len--;
+ }
+ while (str_len > 0 && *str++ == ' ')
+ str_len--;
+
+ return (str_len);
+}
+
+static caddr_t
+cam_quirkmatch(caddr_t target, caddr_t quirk_table, int num_entries,
+ int entry_size, cam_quirkmatch_t *comp_func)
+{
+ for (; num_entries > 0; num_entries--, quirk_table += entry_size) {
+ if ((*comp_func)(target, quirk_table) == 0)
+ return (quirk_table);
+ }
+ return (NULL);
+}
+
+void
+aic_sense_desc(int sense_key, int asc, int ascq,
+ struct scsi_inquiry_data *inq_data,
+ const char **sense_key_desc, const char **asc_desc)
+{
+ const struct asc_table_entry *asc_entry;
+ const struct sense_key_table_entry *sense_entry;
+
+ fetchtableentries(sense_key, asc, ascq,
+ inq_data,
+ &sense_entry,
+ &asc_entry);
+
+ *sense_key_desc = sense_entry->desc;
+
+ if (asc_entry != NULL)
+ *asc_desc = asc_entry->desc;
+ else if (asc >= 0x80 && asc <= 0xff)
+ *asc_desc = "Vendor Specific ASC";
+ else if (ascq >= 0x80 && ascq <= 0xff)
+ *asc_desc = "Vendor Specific ASCQ";
+ else
+ *asc_desc = "Reserved ASC/ASCQ pair";
+}
+
+/*
+ * Given sense and device type information, return the appropriate action.
+ * If we do not understand the specific error as identified by the ASC/ASCQ
+ * pair, fall back on the more generic actions derived from the sense key.
+ */
+aic_sense_action
+aic_sense_error_action(struct scsi_sense_data *sense_data,
+ struct scsi_inquiry_data *inq_data, uint32_t sense_flags)
+{
+ const struct asc_table_entry *asc_entry;
+ const struct sense_key_table_entry *sense_entry;
+ int error_code, sense_key, asc, ascq;
+ aic_sense_action action;
+
+ scsi_extract_sense(sense_data, &error_code, &sense_key, &asc, &ascq);
+
+ if (error_code == SSD_DEFERRED_ERROR) {
+ /*
+ * XXX dufault@FreeBSD.org
+ * This error doesn't relate to the command associated
+ * with this request sense. A deferred error is an error
+ * for a command that has already returned GOOD status
+ * (see SCSI2 8.2.14.2).
+ *
+ * By my reading of that section, it looks like the current
+ * command has been cancelled, we should now clean things up
+ * (hopefully recovering any lost data) and then retry the
+ * current command. There are two easy choices, both wrong:
+ *
+ * 1. Drop through (like we had been doing), thus treating
+ * this as if the error were for the current command and
+ * return and stop the current command.
+ *
+ * 2. Issue a retry (like I made it do) thus hopefully
+ * recovering the current transfer, and ignoring the
+ * fact that we've dropped a command.
+ *
+ * These should probably be handled in a device specific
+ * sense handler or punted back up to a user mode daemon
+ */
+ action = SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE;
+ } else {
+ fetchtableentries(sense_key, asc, ascq,
+ inq_data,
+ &sense_entry,
+ &asc_entry);
+
+ /*
+ * Override the 'No additional Sense' entry (0,0)
+ * with the error action of the sense key.
+ */
+ if (asc_entry != NULL
+ && (asc != 0 || ascq != 0))
+ action = asc_entry->action;
+ else
+ action = sense_entry->action;
+
+ if (sense_key == SSD_KEY_RECOVERED_ERROR) {
+ /*
+ * The action succeeded but the device wants
+ * the user to know that some recovery action
+ * was required.
+ */
+ action &= ~(SS_MASK|SSQ_MASK|SS_ERRMASK);
+ action |= SS_NOP|SSQ_PRINT_SENSE;
+ } else if (sense_key == SSD_KEY_ILLEGAL_REQUEST) {
+ if ((sense_flags & SF_QUIET_IR) != 0)
+ action &= ~SSQ_PRINT_SENSE;
+ } else if (sense_key == SSD_KEY_UNIT_ATTENTION) {
+ if ((sense_flags & SF_RETRY_UA) != 0
+ && (action & SS_MASK) == SS_FAIL) {
+ action &= ~(SS_MASK|SSQ_MASK);
+ action |= SS_RETRY|SSQ_DECREMENT_COUNT|
+ SSQ_PRINT_SENSE;
+ }
+ }
+ }
+
+ if ((sense_flags & SF_PRINT_ALWAYS) != 0)
+ action |= SSQ_PRINT_SENSE;
+ else if ((sense_flags & SF_NO_PRINT) != 0)
+ action &= ~SSQ_PRINT_SENSE;
+
+ return (action);
+}
+
+/*
+ * Try make as good a match as possible with
+ * available sub drivers
+ */
+int
+aic_inquiry_match(caddr_t inqbuffer, caddr_t table_entry)
+{
+ struct scsi_inquiry_pattern *entry;
+ struct scsi_inquiry_data *inq;
+
+ entry = (struct scsi_inquiry_pattern *)table_entry;
+ inq = (struct scsi_inquiry_data *)inqbuffer;
+
+ if (((SID_TYPE(inq) == entry->type)
+ || (entry->type == T_ANY))
+ && (SID_IS_REMOVABLE(inq) ? entry->media_type & SIP_MEDIA_REMOVABLE
+ : entry->media_type & SIP_MEDIA_FIXED)
+ && (cam_strmatch(inq->vendor, entry->vendor, sizeof(inq->vendor)) == 0)
+ && (cam_strmatch(inq->product, entry->product,
+ sizeof(inq->product)) == 0)
+ && (cam_strmatch(inq->revision, entry->revision,
+ sizeof(inq->revision)) == 0)) {
+ return (0);
+ }
+ return (-1);
+}
+
+/*
+ * 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.
+ */
+u_int
+aic_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));
+}
diff --git a/drivers/scsi/aic7xxx/aiclib.h b/drivers/scsi/aic7xxx/aiclib.h
new file mode 100644
index 000000000000..2752baabee47
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aiclib.h
@@ -0,0 +1,992 @@
+/*
+ * Largely written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $FreeBSD: src/sys/cam/scsi/scsi_all.h,v 1.21 2002/10/08 17:12:44 ken Exp $
+ * $Id$
+ */
+
+/*
+ * SCSI general interface description
+ */
+
+#ifndef _SCSI_SCSI_ALL_H
+#define _SCSI_SCSI_ALL_H 1
+
+/*
+ * SCSI command format
+ */
+
+/*
+ * Define dome bits that are in ALL (or a lot of) scsi commands
+ */
+#define SCSI_CTL_LINK 0x01
+#define SCSI_CTL_FLAG 0x02
+#define SCSI_CTL_VENDOR 0xC0
+#define SCSI_CMD_LUN 0xA0 /* these two should not be needed */
+#define SCSI_CMD_LUN_SHIFT 5 /* LUN in the cmd is no longer SCSI */
+
+#define SCSI_MAX_CDBLEN 16 /*
+ * 16 byte commands are in the
+ * SCSI-3 spec
+ */
+/* 6byte CDBs special case 0 length to be 256 */
+#define SCSI_CDB6_LEN(len) ((len) == 0 ? 256 : len)
+
+/*
+ * This type defines actions to be taken when a particular sense code is
+ * received. Right now, these flags are only defined to take up 16 bits,
+ * but can be expanded in the future if necessary.
+ */
+typedef enum {
+ SS_NOP = 0x000000, /* Do nothing */
+ SS_RETRY = 0x010000, /* Retry the command */
+ SS_FAIL = 0x020000, /* Bail out */
+ SS_START = 0x030000, /* Send a Start Unit command to the device,
+ * then retry the original command.
+ */
+ SS_TUR = 0x040000, /* Send a Test Unit Ready command to the
+ * device, then retry the original command.
+ */
+ SS_REQSENSE = 0x050000, /* Send a RequestSense command to the
+ * device, then retry the original command.
+ */
+ SS_INQ_REFRESH = 0x060000,
+ SS_MASK = 0xff0000
+} aic_sense_action;
+
+typedef enum {
+ SSQ_NONE = 0x0000,
+ SSQ_DECREMENT_COUNT = 0x0100, /* Decrement the retry count */
+ SSQ_MANY = 0x0200, /* send lots of recovery commands */
+ SSQ_RANGE = 0x0400, /*
+ * This table entry represents the
+ * end of a range of ASCQs that
+ * have identical error actions
+ * and text.
+ */
+ SSQ_PRINT_SENSE = 0x0800,
+ SSQ_DELAY = 0x1000, /* Delay before retry. */
+ SSQ_DELAY_RANDOM = 0x2000, /* Randomized delay before retry. */
+ SSQ_FALLBACK = 0x4000, /* Do a speed fallback to recover */
+ SSQ_MASK = 0xff00
+} aic_sense_action_qualifier;
+
+/* Mask for error status values */
+#define SS_ERRMASK 0xff
+
+/* The default, retyable, error action */
+#define SS_RDEF SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE|EIO
+
+/* The retyable, error action, with table specified error code */
+#define SS_RET SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE
+
+/* Fatal error action, with table specified error code */
+#define SS_FATAL SS_FAIL|SSQ_PRINT_SENSE
+
+struct scsi_generic
+{
+ uint8_t opcode;
+ uint8_t bytes[11];
+};
+
+struct scsi_request_sense
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_test_unit_ready
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[3];
+ uint8_t control;
+};
+
+struct scsi_send_diag
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SSD_UOL 0x01
+#define SSD_DOL 0x02
+#define SSD_SELFTEST 0x04
+#define SSD_PF 0x10
+ uint8_t unused[1];
+ uint8_t paramlen[2];
+ uint8_t control;
+};
+
+struct scsi_sense
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_inquiry
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SI_EVPD 0x01
+ uint8_t page_code;
+ uint8_t reserved;
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_mode_sense_6
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SMS_DBD 0x08
+ uint8_t page;
+#define SMS_PAGE_CODE 0x3F
+#define SMS_VENDOR_SPECIFIC_PAGE 0x00
+#define SMS_DISCONNECT_RECONNECT_PAGE 0x02
+#define SMS_PERIPHERAL_DEVICE_PAGE 0x09
+#define SMS_CONTROL_MODE_PAGE 0x0A
+#define SMS_ALL_PAGES_PAGE 0x3F
+#define SMS_PAGE_CTRL_MASK 0xC0
+#define SMS_PAGE_CTRL_CURRENT 0x00
+#define SMS_PAGE_CTRL_CHANGEABLE 0x40
+#define SMS_PAGE_CTRL_DEFAULT 0x80
+#define SMS_PAGE_CTRL_SAVED 0xC0
+ uint8_t unused;
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_mode_sense_10
+{
+ uint8_t opcode;
+ uint8_t byte2; /* same bits as small version */
+ uint8_t page; /* same bits as small version */
+ uint8_t unused[4];
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_mode_select_6
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SMS_SP 0x01
+#define SMS_PF 0x10
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_mode_select_10
+{
+ uint8_t opcode;
+ uint8_t byte2; /* same bits as small version */
+ uint8_t unused[5];
+ uint8_t length[2];
+ uint8_t control;
+};
+
+/*
+ * When sending a mode select to a tape drive, the medium type must be 0.
+ */
+struct scsi_mode_hdr_6
+{
+ uint8_t datalen;
+ uint8_t medium_type;
+ uint8_t dev_specific;
+ uint8_t block_descr_len;
+};
+
+struct scsi_mode_hdr_10
+{
+ uint8_t datalen[2];
+ uint8_t medium_type;
+ uint8_t dev_specific;
+ uint8_t reserved[2];
+ uint8_t block_descr_len[2];
+};
+
+struct scsi_mode_block_descr
+{
+ uint8_t density_code;
+ uint8_t num_blocks[3];
+ uint8_t reserved;
+ uint8_t block_len[3];
+};
+
+struct scsi_log_sense
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SLS_SP 0x01
+#define SLS_PPC 0x02
+ uint8_t page;
+#define SLS_PAGE_CODE 0x3F
+#define SLS_ALL_PAGES_PAGE 0x00
+#define SLS_OVERRUN_PAGE 0x01
+#define SLS_ERROR_WRITE_PAGE 0x02
+#define SLS_ERROR_READ_PAGE 0x03
+#define SLS_ERROR_READREVERSE_PAGE 0x04
+#define SLS_ERROR_VERIFY_PAGE 0x05
+#define SLS_ERROR_NONMEDIUM_PAGE 0x06
+#define SLS_ERROR_LASTN_PAGE 0x07
+#define SLS_PAGE_CTRL_MASK 0xC0
+#define SLS_PAGE_CTRL_THRESHOLD 0x00
+#define SLS_PAGE_CTRL_CUMULATIVE 0x40
+#define SLS_PAGE_CTRL_THRESH_DEFAULT 0x80
+#define SLS_PAGE_CTRL_CUMUL_DEFAULT 0xC0
+ uint8_t reserved[2];
+ uint8_t paramptr[2];
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_log_select
+{
+ uint8_t opcode;
+ uint8_t byte2;
+/* SLS_SP 0x01 */
+#define SLS_PCR 0x02
+ uint8_t page;
+/* SLS_PAGE_CTRL_MASK 0xC0 */
+/* SLS_PAGE_CTRL_THRESHOLD 0x00 */
+/* SLS_PAGE_CTRL_CUMULATIVE 0x40 */
+/* SLS_PAGE_CTRL_THRESH_DEFAULT 0x80 */
+/* SLS_PAGE_CTRL_CUMUL_DEFAULT 0xC0 */
+ uint8_t reserved[4];
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_log_header
+{
+ uint8_t page;
+ uint8_t reserved;
+ uint8_t datalen[2];
+};
+
+struct scsi_log_param_header {
+ uint8_t param_code[2];
+ uint8_t param_control;
+#define SLP_LP 0x01
+#define SLP_LBIN 0x02
+#define SLP_TMC_MASK 0x0C
+#define SLP_TMC_ALWAYS 0x00
+#define SLP_TMC_EQUAL 0x04
+#define SLP_TMC_NOTEQUAL 0x08
+#define SLP_TMC_GREATER 0x0C
+#define SLP_ETC 0x10
+#define SLP_TSD 0x20
+#define SLP_DS 0x40
+#define SLP_DU 0x80
+ uint8_t param_len;
+};
+
+struct scsi_control_page {
+ uint8_t page_code;
+ uint8_t page_length;
+ uint8_t rlec;
+#define SCB_RLEC 0x01 /*Report Log Exception Cond*/
+ uint8_t queue_flags;
+#define SCP_QUEUE_ALG_MASK 0xF0
+#define SCP_QUEUE_ALG_RESTRICTED 0x00
+#define SCP_QUEUE_ALG_UNRESTRICTED 0x10
+#define SCP_QUEUE_ERR 0x02 /*Queued I/O aborted for CACs*/
+#define SCP_QUEUE_DQUE 0x01 /*Queued I/O disabled*/
+ uint8_t eca_and_aen;
+#define SCP_EECA 0x80 /*Enable Extended CA*/
+#define SCP_RAENP 0x04 /*Ready AEN Permission*/
+#define SCP_UAAENP 0x02 /*UA AEN Permission*/
+#define SCP_EAENP 0x01 /*Error AEN Permission*/
+ uint8_t reserved;
+ uint8_t aen_holdoff_period[2];
+};
+
+struct scsi_reserve
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_release
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_prevent
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t how;
+ uint8_t control;
+};
+#define PR_PREVENT 0x01
+#define PR_ALLOW 0x00
+
+struct scsi_sync_cache
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t begin_lba[4];
+ uint8_t reserved;
+ uint8_t lb_count[2];
+ uint8_t control;
+};
+
+
+struct scsi_changedef
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused1;
+ uint8_t how;
+ uint8_t unused[4];
+ uint8_t datalen;
+ uint8_t control;
+};
+
+struct scsi_read_buffer
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define RWB_MODE 0x07
+#define RWB_MODE_HDR_DATA 0x00
+#define RWB_MODE_DATA 0x02
+#define RWB_MODE_DOWNLOAD 0x04
+#define RWB_MODE_DOWNLOAD_SAVE 0x05
+ uint8_t buffer_id;
+ uint8_t offset[3];
+ uint8_t length[3];
+ uint8_t control;
+};
+
+struct scsi_write_buffer
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t buffer_id;
+ uint8_t offset[3];
+ uint8_t length[3];
+ uint8_t control;
+};
+
+struct scsi_rw_6
+{
+ uint8_t opcode;
+ uint8_t addr[3];
+/* only 5 bits are valid in the MSB address byte */
+#define SRW_TOPADDR 0x1F
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_rw_10
+{
+ uint8_t opcode;
+#define SRW10_RELADDR 0x01
+#define SRW10_FUA 0x08
+#define SRW10_DPO 0x10
+ uint8_t byte2;
+ uint8_t addr[4];
+ uint8_t reserved;
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_rw_12
+{
+ uint8_t opcode;
+#define SRW12_RELADDR 0x01
+#define SRW12_FUA 0x08
+#define SRW12_DPO 0x10
+ uint8_t byte2;
+ uint8_t addr[4];
+ uint8_t length[4];
+ uint8_t reserved;
+ uint8_t control;
+};
+
+struct scsi_start_stop_unit
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SSS_IMMED 0x01
+ uint8_t reserved[2];
+ uint8_t how;
+#define SSS_START 0x01
+#define SSS_LOEJ 0x02
+ uint8_t control;
+};
+
+#define SC_SCSI_1 0x01
+#define SC_SCSI_2 0x03
+
+/*
+ * Opcodes
+ */
+
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define READ_6 0x08
+#define WRITE_6 0x0a
+#define INQUIRY 0x12
+#define MODE_SELECT_6 0x15
+#define MODE_SENSE_6 0x1a
+#define START_STOP_UNIT 0x1b
+#define START_STOP 0x1b
+#define RESERVE 0x16
+#define RELEASE 0x17
+#define RECEIVE_DIAGNOSTIC 0x1c
+#define SEND_DIAGNOSTIC 0x1d
+#define PREVENT_ALLOW 0x1e
+#define READ_CAPACITY 0x25
+#define READ_10 0x28
+#define WRITE_10 0x2a
+#define POSITION_TO_ELEMENT 0x2b
+#define SYNCHRONIZE_CACHE 0x35
+#define WRITE_BUFFER 0x3b
+#define READ_BUFFER 0x3c
+#define CHANGE_DEFINITION 0x40
+#define LOG_SELECT 0x4c
+#define LOG_SENSE 0x4d
+#ifdef XXXCAM
+#define MODE_SENSE_10 0x5A
+#endif
+#define MODE_SELECT_10 0x55
+#define MOVE_MEDIUM 0xa5
+#define READ_12 0xa8
+#define WRITE_12 0xaa
+#define READ_ELEMENT_STATUS 0xb8
+
+
+/*
+ * Device Types
+ */
+#define T_DIRECT 0x00
+#define T_SEQUENTIAL 0x01
+#define T_PRINTER 0x02
+#define T_PROCESSOR 0x03
+#define T_WORM 0x04
+#define T_CDROM 0x05
+#define T_SCANNER 0x06
+#define T_OPTICAL 0x07
+#define T_CHANGER 0x08
+#define T_COMM 0x09
+#define T_ASC0 0x0a
+#define T_ASC1 0x0b
+#define T_STORARRAY 0x0c
+#define T_ENCLOSURE 0x0d
+#define T_RBC 0x0e
+#define T_OCRW 0x0f
+#define T_NODEVICE 0x1F
+#define T_ANY 0xFF /* Used in Quirk table matches */
+
+#define T_REMOV 1
+#define T_FIXED 0
+
+/*
+ * This length is the initial inquiry length used by the probe code, as
+ * well as the legnth necessary for aic_print_inquiry() to function
+ * correctly. If either use requires a different length in the future,
+ * the two values should be de-coupled.
+ */
+#define SHORT_INQUIRY_LENGTH 36
+
+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
+ uint8_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];
+};
+
+struct scsi_vpd_unit_serial_number
+{
+ uint8_t device;
+ uint8_t page_code;
+#define SVPD_UNIT_SERIAL_NUMBER 0x80
+ uint8_t reserved;
+ uint8_t length; /* serial number length */
+#define SVPD_SERIAL_NUM_SIZE 251
+ uint8_t serial_num[SVPD_SERIAL_NUM_SIZE];
+};
+
+struct scsi_read_capacity
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t addr[4];
+ uint8_t unused[3];
+ uint8_t control;
+};
+
+struct scsi_read_capacity_data
+{
+ uint8_t addr[4];
+ uint8_t length[4];
+};
+
+struct scsi_report_luns
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[3];
+ uint8_t addr[4];
+ uint8_t control;
+};
+
+struct scsi_report_luns_data {
+ uint8_t length[4]; /* length of LUN inventory, in bytes */
+ uint8_t reserved[4]; /* unused */
+ /*
+ * LUN inventory- we only support the type zero form for now.
+ */
+ struct {
+ uint8_t lundata[8];
+ } luns[1];
+};
+#define RPL_LUNDATA_ATYP_MASK 0xc0 /* MBZ for type 0 lun */
+#define RPL_LUNDATA_T0LUN 1 /* @ lundata[1] */
+
+
+struct scsi_sense_data
+{
+ uint8_t error_code;
+#define SSD_ERRCODE 0x7F
+#define SSD_CURRENT_ERROR 0x70
+#define SSD_DEFERRED_ERROR 0x71
+#define SSD_ERRCODE_VALID 0x80
+ uint8_t segment;
+ uint8_t flags;
+#define SSD_KEY 0x0F
+#define SSD_KEY_NO_SENSE 0x00
+#define SSD_KEY_RECOVERED_ERROR 0x01
+#define SSD_KEY_NOT_READY 0x02
+#define SSD_KEY_MEDIUM_ERROR 0x03
+#define SSD_KEY_HARDWARE_ERROR 0x04
+#define SSD_KEY_ILLEGAL_REQUEST 0x05
+#define SSD_KEY_UNIT_ATTENTION 0x06
+#define SSD_KEY_DATA_PROTECT 0x07
+#define SSD_KEY_BLANK_CHECK 0x08
+#define SSD_KEY_Vendor_Specific 0x09
+#define SSD_KEY_COPY_ABORTED 0x0a
+#define SSD_KEY_ABORTED_COMMAND 0x0b
+#define SSD_KEY_EQUAL 0x0c
+#define SSD_KEY_VOLUME_OVERFLOW 0x0d
+#define SSD_KEY_MISCOMPARE 0x0e
+#define SSD_KEY_RESERVED 0x0f
+#define SSD_ILI 0x20
+#define SSD_EOM 0x40
+#define SSD_FILEMARK 0x80
+ 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];
+#define SSD_SCS_VALID 0x80
+#define SSD_FIELDPTR_CMD 0x40
+#define SSD_BITPTR_VALID 0x08
+#define SSD_BITPTR_VALUE 0x07
+#define SSD_MIN_SIZE 18
+ uint8_t extra_bytes[14];
+#define SSD_FULL_SIZE sizeof(struct scsi_sense_data)
+};
+
+struct scsi_mode_header_6
+{
+ uint8_t data_length; /* Sense data length */
+ uint8_t medium_type;
+ uint8_t dev_spec;
+ uint8_t blk_desc_len;
+};
+
+struct scsi_mode_header_10
+{
+ uint8_t data_length[2];/* Sense data length */
+ uint8_t medium_type;
+ uint8_t dev_spec;
+ uint8_t unused[2];
+ uint8_t blk_desc_len[2];
+};
+
+struct scsi_mode_page_header
+{
+ uint8_t page_code;
+ uint8_t page_length;
+};
+
+struct scsi_mode_blk_desc
+{
+ uint8_t density;
+ uint8_t nblocks[3];
+ uint8_t reserved;
+ uint8_t blklen[3];
+};
+
+#define SCSI_DEFAULT_DENSITY 0x00 /* use 'default' density */
+#define SCSI_SAME_DENSITY 0x7f /* use 'same' density- >= SCSI-2 only */
+
+
+/*
+ * Status Byte
+ */
+#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 /* Obsolete in SAM-2 */
+#define SCSI_STATUS_QUEUE_FULL 0x28
+#define SCSI_STATUS_ACA_ACTIVE 0x30
+#define SCSI_STATUS_TASK_ABORTED 0x40
+
+struct scsi_inquiry_pattern {
+ uint8_t type;
+ uint8_t media_type;
+#define SIP_MEDIA_REMOVABLE 0x01
+#define SIP_MEDIA_FIXED 0x02
+ const char *vendor;
+ const char *product;
+ const char *revision;
+};
+
+struct scsi_static_inquiry_pattern {
+ uint8_t type;
+ uint8_t media_type;
+ char vendor[SID_VENDOR_SIZE+1];
+ char product[SID_PRODUCT_SIZE+1];
+ char revision[SID_REVISION_SIZE+1];
+};
+
+struct scsi_sense_quirk_entry {
+ struct scsi_inquiry_pattern inq_pat;
+ int num_sense_keys;
+ int num_ascs;
+ struct sense_key_table_entry *sense_key_info;
+ struct asc_table_entry *asc_info;
+};
+
+struct sense_key_table_entry {
+ uint8_t sense_key;
+ uint32_t action;
+ const char *desc;
+};
+
+struct asc_table_entry {
+ uint8_t asc;
+ uint8_t ascq;
+ uint32_t action;
+ const char *desc;
+};
+
+struct op_table_entry {
+ uint8_t opcode;
+ uint16_t opmask;
+ const char *desc;
+};
+
+struct scsi_op_quirk_entry {
+ struct scsi_inquiry_pattern inq_pat;
+ int num_ops;
+ struct op_table_entry *op_table;
+};
+
+typedef enum {
+ SSS_FLAG_NONE = 0x00,
+ SSS_FLAG_PRINT_COMMAND = 0x01
+} scsi_sense_string_flags;
+
+extern const char *scsi_sense_key_text[];
+
+/************************* Large Disk Handling ********************************/
+static __inline int aic_sector_div(u_long capacity, int heads, int sectors);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+static __inline int
+aic_sector_div(u_long capacity, int heads, int sectors)
+{
+ return (capacity / (heads * sectors));
+}
+#else
+static __inline int
+aic_sector_div(sector_t capacity, int heads, int sectors)
+{
+ /* ugly, ugly sector_div calling convention.. */
+ sector_div(capacity, (heads * sectors));
+ return (int)capacity;
+}
+#endif
+
+/**************************** Module Library Hack *****************************/
+/*
+ * What we'd like to do is have a single "scsi library" module that both the
+ * aic7xxx and aic79xx drivers could load and depend on. A cursory examination
+ * of implementing module dependencies in Linux (handling the install and
+ * initrd cases) does not look promissing. For now, we just duplicate this
+ * code in both drivers using a simple symbol renaming scheme that hides this
+ * hack from the drivers.
+ */
+#define AIC_LIB_ENTRY_CONCAT(x, prefix) prefix ## x
+#define AIC_LIB_ENTRY_EXPAND(x, prefix) AIC_LIB_ENTRY_CONCAT(x, prefix)
+#define AIC_LIB_ENTRY(x) AIC_LIB_ENTRY_EXPAND(x, AIC_LIB_PREFIX)
+
+#define aic_sense_desc AIC_LIB_ENTRY(_sense_desc)
+#define aic_sense_error_action AIC_LIB_ENTRY(_sense_error_action)
+#define aic_op_desc AIC_LIB_ENTRY(_op_desc)
+#define aic_cdb_string AIC_LIB_ENTRY(_cdb_string)
+#define aic_print_inquiry AIC_LIB_ENTRY(_print_inquiry)
+#define aic_calc_syncsrate AIC_LIB_ENTRY(_calc_syncrate)
+#define aic_calc_syncparam AIC_LIB_ENTRY(_calc_syncparam)
+#define aic_inquiry_match AIC_LIB_ENTRY(_inquiry_match)
+#define aic_static_inquiry_match AIC_LIB_ENTRY(_static_inquiry_match)
+
+/******************************************************************************/
+
+void aic_sense_desc(int /*sense_key*/, int /*asc*/,
+ int /*ascq*/, struct scsi_inquiry_data*,
+ const char** /*sense_key_desc*/,
+ const char** /*asc_desc*/);
+aic_sense_action aic_sense_error_action(struct scsi_sense_data*,
+ struct scsi_inquiry_data*,
+ uint32_t /*sense_flags*/);
+
+#define SF_RETRY_UA 0x01
+#define SF_NO_PRINT 0x02
+#define SF_QUIET_IR 0x04 /* Be quiet about Illegal Request reponses */
+#define SF_PRINT_ALWAYS 0x08
+
+
+const char * aic_op_desc(uint16_t /*opcode*/, struct scsi_inquiry_data*);
+char * aic_cdb_string(uint8_t* /*cdb_ptr*/, char* /*cdb_string*/,
+ size_t /*len*/);
+void aic_print_inquiry(struct scsi_inquiry_data*);
+
+u_int aic_calc_syncsrate(u_int /*period_factor*/);
+u_int aic_calc_syncparam(u_int /*period*/);
+
+int aic_inquiry_match(caddr_t /*inqbuffer*/,
+ caddr_t /*table_entry*/);
+int aic_static_inquiry_match(caddr_t /*inqbuffer*/,
+ caddr_t /*table_entry*/);
+
+
+static __inline void scsi_extract_sense(struct scsi_sense_data *sense,
+ int *error_code, int *sense_key,
+ int *asc, int *ascq);
+static __inline void scsi_ulto2b(uint32_t val, uint8_t *bytes);
+static __inline void scsi_ulto3b(uint32_t val, uint8_t *bytes);
+static __inline void scsi_ulto4b(uint32_t val, uint8_t *bytes);
+static __inline uint32_t scsi_2btoul(uint8_t *bytes);
+static __inline uint32_t scsi_3btoul(uint8_t *bytes);
+static __inline int32_t scsi_3btol(uint8_t *bytes);
+static __inline uint32_t scsi_4btoul(uint8_t *bytes);
+
+static __inline void scsi_extract_sense(struct scsi_sense_data *sense,
+ int *error_code, int *sense_key,
+ int *asc, int *ascq)
+{
+ *error_code = sense->error_code & SSD_ERRCODE;
+ *sense_key = sense->flags & SSD_KEY;
+ *asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0;
+ *ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0;
+}
+
+static __inline void
+scsi_ulto2b(uint32_t val, uint8_t *bytes)
+{
+
+ bytes[0] = (val >> 8) & 0xff;
+ bytes[1] = val & 0xff;
+}
+
+static __inline void
+scsi_ulto3b(uint32_t val, uint8_t *bytes)
+{
+
+ bytes[0] = (val >> 16) & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = val & 0xff;
+}
+
+static __inline void
+scsi_ulto4b(uint32_t val, uint8_t *bytes)
+{
+
+ bytes[0] = (val >> 24) & 0xff;
+ bytes[1] = (val >> 16) & 0xff;
+ bytes[2] = (val >> 8) & 0xff;
+ bytes[3] = val & 0xff;
+}
+
+static __inline uint32_t
+scsi_2btoul(uint8_t *bytes)
+{
+ uint32_t rv;
+
+ rv = (bytes[0] << 8) |
+ bytes[1];
+ return (rv);
+}
+
+static __inline uint32_t
+scsi_3btoul(uint8_t *bytes)
+{
+ uint32_t rv;
+
+ rv = (bytes[0] << 16) |
+ (bytes[1] << 8) |
+ bytes[2];
+ return (rv);
+}
+
+static __inline int32_t
+scsi_3btol(uint8_t *bytes)
+{
+ uint32_t rc = scsi_3btoul(bytes);
+
+ if (rc & 0x00800000)
+ rc |= 0xff000000;
+
+ return (int32_t) rc;
+}
+
+static __inline uint32_t
+scsi_4btoul(uint8_t *bytes)
+{
+ uint32_t rv;
+
+ rv = (bytes[0] << 24) |
+ (bytes[1] << 16) |
+ (bytes[2] << 8) |
+ bytes[3];
+ return (rv);
+}
+
+#endif /*_SCSI_SCSI_ALL_H*/
diff --git a/drivers/scsi/aic7xxx/cam.h b/drivers/scsi/aic7xxx/cam.h
index d7a526fe9989..b9ba333a10e5 100644
--- a/drivers/scsi/aic7xxx/cam.h
+++ b/drivers/scsi/aic7xxx/cam.h
@@ -29,13 +29,13 @@
* 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 $
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/cam.h#15 $
*/
#ifndef _AIC7XXX_CAM_H
#define _AIC7XXX_CAM_H 1
-/* Provide a mapping from CAM constructs to Linux SCSI constructs */
+#include <linux/types.h>
#define CAM_BUS_WILDCARD ((u_int)~0)
#define CAM_TARGET_WILDCARD ((u_int)~0)
@@ -43,56 +43,34 @@
/* 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,
+ CAM_REQ_INPROG, /* CCB request is in progress */
+ CAM_REQ_CMP, /* CCB request completed without error */
+ CAM_REQ_ABORTED, /* CCB request aborted by the host */
+ CAM_UA_ABORT, /* Unable to abort CCB request */
+ CAM_REQ_CMP_ERR, /* CCB request completed with an error */
+ CAM_BUSY, /* CAM subsytem is busy */
+ CAM_REQ_INVALID, /* CCB request was invalid */
+ CAM_PATH_INVALID, /* Supplied Path ID is invalid */
+ CAM_SEL_TIMEOUT, /* Target Selection Timeout */
+ CAM_CMD_TIMEOUT, /* Command timeout */
+ CAM_SCSI_STATUS_ERROR, /* SCSI error, look at error code in CCB */
+ CAM_SCSI_BUS_RESET, /* SCSI Bus Reset Sent/Received */
+ CAM_UNCOR_PARITY, /* Uncorrectable parity error occurred */
+ CAM_AUTOSENSE_FAIL, /* Autosense: request sense cmd fail */
+ CAM_NO_HBA, /* No HBA Detected Error */
+ CAM_DATA_RUN_ERR, /* Data Overrun error */
+ CAM_UNEXP_BUSFREE, /* Unexpected Bus Free */
+ CAM_SEQUENCE_FAIL, /* Protocol Violation */
+ CAM_CCB_LEN_ERR, /* CCB length supplied is inadequate */
+ CAM_PROVIDE_FAIL, /* Unable to provide requested capability */
+ CAM_BDR_SENT, /* A SCSI BDR msg was sent to target */
+ CAM_REQ_TERMIO, /* CCB request terminated by the host */
+ CAM_UNREC_HBA_ERROR, /* Unrecoverable Host Bus Adapter Error */
+ CAM_REQ_TOO_BIG, /* The request was too large for this host */
+ CAM_UA_TERMIO, /* Unable to terminate I/O CCB request */
+ CAM_MSG_REJECT_REC, /* Message Reject Received */
+ CAM_DEV_NOT_THERE, /* SCSI Device Not Installed/there */
+ CAM_RESRC_UNAVAIL, /* Resource Unavailable */
/*
* This request should be requeued to preserve
* transaction ordering. This typically occurs
@@ -101,7 +79,8 @@ typedef enum {
* requests for the target at the sim level
* back into the XPT queue.
*/
- CAM_REQUEUE_REQ = DID_BUS_BUSY,
+ CAM_REQUEUE_REQ,
+ CAM_DEV_QFRZN = 0x40,
CAM_STATUS_MASK = 0x3F
} cam_status;
diff --git a/drivers/scsi/aic7xxx/scsi_iu.h b/drivers/scsi/aic7xxx/scsi_iu.h
new file mode 100644
index 000000000000..801da0af4c12
--- /dev/null
+++ b/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/drivers/scsi/aic7xxx/scsi_message.h b/drivers/scsi/aic7xxx/scsi_message.h
index a2dffd2b7aa0..75811e245ec7 100644
--- a/drivers/scsi/aic7xxx/scsi_message.h
+++ b/drivers/scsi/aic7xxx/scsi_message.h
@@ -46,7 +46,7 @@
#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 0x03F
+#define MSG_IDENTIFY_LUNMASK 0x3F
/* Extended messages (opcode and length) */
#define MSG_EXT_SDTR 0x01
@@ -60,6 +60,11 @@
#define MSG_EXT_PPR 0x04 /* SPI3 */
#define MSG_EXT_PPR_LEN 0x06
-#define MSG_EXT_PPR_QAS_REQ 0x04
-#define MSG_EXT_PPR_DT_REQ 0x02
+#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/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
index 8b2dd70f6bb3..5b53fcbd91a9 100644
--- a/drivers/scsi/hosts.h
+++ b/drivers/scsi/hosts.h
@@ -554,9 +554,6 @@ struct Scsi_Device_Template
struct device_driver scsi_driverfs_driver;
};
-void scsi_initialize_queue(Scsi_Device *, struct Scsi_Host *);
-
-
/*
* Highlevel driver registration/unregistration.
*/
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index f800f95ba320..358e66e3d9ef 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -28,9 +28,10 @@
* Ver 0.8 Feb 05 99 Optical media need translation too. Reverse 0.7.
* Ver 0.9 Jul 04 99 Fix a bug in SG_SET_TRANSFORM.
* Ver 0.91 Jun 10 02 Fix "off by one" error in transforms
+ * Ver 0.92 Dec 31 02 Implement new SCSI mid level API
*/
-#define IDESCSI_VERSION "0.9"
+#define IDESCSI_VERSION "0.92"
#include <linux/module.h>
#include <linux/config.h>
@@ -259,6 +260,42 @@ static void hexdump(u8 *x, int len)
printk("]\n");
}
+static int idescsi_check_condition(ide_drive_t *drive, struct request *failed_command)
+{
+ idescsi_scsi_t *scsi = drive->driver_data;
+ idescsi_pc_t *pc;
+ struct request *rq;
+ u8 *buf;
+
+ /* stuff a sense request in front of our current request */
+ pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);
+ rq = kmalloc (sizeof (struct request), GFP_ATOMIC);
+ buf = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_ATOMIC);
+ if (pc == NULL || rq == NULL || buf == NULL) {
+ if (pc) kfree(pc);
+ if (rq) kfree(rq);
+ if (buf) kfree(buf);
+ return -ENOMEM;
+ }
+ memset (pc, 0, sizeof (idescsi_pc_t));
+ memset (buf, 0, SCSI_SENSE_BUFFERSIZE);
+ ide_init_drive_cmd(rq);
+ rq->special = (char *) pc;
+ pc->rq = rq;
+ pc->buffer = buf;
+ pc->c[0] = REQUEST_SENSE;
+ pc->c[4] = pc->request_transfer = pc->buffer_size = SCSI_SENSE_BUFFERSIZE;
+ rq->flags = REQ_SENSE;
+ pc->timeout = jiffies + WAIT_READY;
+ /* NOTE! Save the failed packet command in "rq->buffer" */
+ rq->buffer = (void *) failed_command->special;
+ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) {
+ printk ("ide-scsi: %s: queue cmd = ", drive->name);
+ hexdump(pc->c, 6);
+ }
+ return ide_do_drive_cmd(drive, rq, ide_preempt);
+}
+
static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
{
idescsi_scsi_t *scsi = drive->driver_data;
@@ -269,19 +306,35 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
u8 *scsi_buf;
unsigned long flags;
- if (!(rq->flags & REQ_SPECIAL)) {
+ if (!(rq->flags & (REQ_SPECIAL|REQ_SENSE))) {
ide_end_request(drive, uptodate, nrsecs);
return 0;
}
ide_end_drive_cmd (drive, 0, 0);
- if (rq->errors >= ERROR_MAX) {
+ if (rq->flags & REQ_SENSE) {
+ idescsi_pc_t *opc = (idescsi_pc_t *) rq->buffer;
+ if (log) {
+ printk ("ide-scsi: %s: wrap up check %lu, rst = ", drive->name, opc->scsi_cmd->serial_number);
+ hexdump(pc->buffer,16);
+ }
+ memcpy((void *) opc->scsi_cmd->sense_buffer, pc->buffer, SCSI_SENSE_BUFFERSIZE);
+ kfree(pc->buffer);
+ kfree(pc);
+ kfree(rq);
+ pc = opc;
+ rq = pc->rq;
+ pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
+ } else if (rq->errors >= ERROR_MAX) {
pc->scsi_cmd->result = DID_ERROR << 16;
if (log)
printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number);
} else if (rq->errors) {
- pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
if (log)
printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+ if (!idescsi_check_condition(drive, rq))
+ /* we started a request sense, so we'll be back, exit for now */
+ return 0;
+ pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
} else {
pc->scsi_cmd->result = DID_OK << 16;
idescsi_transform_pc2 (drive, pc);
@@ -489,11 +542,11 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, sector_t block)
{
#if IDESCSI_DEBUG_LOG
- printk (KERN_INFO "rq_status: %d, dev: %s, cmd: %d, errors: %d\n",rq->rq_status, rq->rq_disk->disk_name,rq->cmd,rq->errors);
- printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+ printk (KERN_INFO "rq_status: %d, dev: %s, cmd: %x, errors: %d\n",rq->rq_status, rq->rq_disk->disk_name,rq->cmd[0],rq->errors);
+ printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %d\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
#endif /* IDESCSI_DEBUG_LOG */
- if (rq->flags & REQ_SPECIAL) {
+ if (rq->flags & (REQ_SPECIAL|REQ_SENSE)) {
return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->special);
}
blk_dump_rq_flags(rq, "ide-scsi: unsup command");
@@ -629,35 +682,10 @@ failed:
return 1;
}
-int idescsi_detect (Scsi_Host_Template *host_template)
+int idescsi_slave_configure(Scsi_Device * sdp)
{
- struct Scsi_Host *host;
- int id;
- int last_lun = 0;
-
- host_template->proc_name = "ide-scsi";
- host = scsi_register(host_template, 0);
- if(host == NULL)
- return 0;
-
- for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++)
- last_lun = IDE_MAX(last_lun, idescsi_drives[id]->last_lun);
- host->max_id = id;
- host->max_lun = last_lun + 1;
- host->can_queue = host->cmd_per_lun * id;
- return 1;
-}
-
-int idescsi_release (struct Scsi_Host *host)
-{
- ide_drive_t *drive;
- int id;
-
- for (id = 0; id < MAX_HWIFS * MAX_DRIVES; id++) {
- drive = idescsi_drives[id];
- if (drive)
- DRIVER(drive)->busy--;
- }
+ /* Configure detected device */
+ scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, sdp->host->cmd_per_lun);
return 0;
}
@@ -832,17 +860,67 @@ abort:
if (rq) kfree (rq);
cmd->result = DID_ERROR << 16;
done(cmd);
- return 0;
+ return 1;
}
int idescsi_abort (Scsi_Cmnd *cmd)
{
- return SCSI_ABORT_SNOOZE;
+ int countdown = 8;
+ unsigned long flags;
+ ide_drive_t *drive = idescsi_drives[cmd->target];
+ idescsi_scsi_t *scsi;
+
+ printk (KERN_ERR "ide-scsi: abort called for %lu\n", cmd->serial_number);
+ if (drive && (scsi = drive->driver_data))
+ while (countdown--) {
+ /* is cmd active?
+ * need to lock so this stuff doesn't change under us */
+ spin_lock_irqsave(&ide_lock, flags);
+ if (scsi->pc && scsi->pc->scsi_cmd->serial_number == cmd->serial_number) {
+ /* yep - let's give it some more time -
+ * we can do that, we're in _our_ error kernel thread */
+ spin_unlock_irqrestore(&ide_lock, flags);
+ scsi_sleep(HZ);
+ continue;
+ }
+ /* no, but is it queued in the ide subsystem? */
+ if (elv_queue_empty(&drive->queue)) {
+ spin_unlock_irqrestore(&ide_lock, flags);
+ return SUCCESS;
+ }
+ spin_unlock_irqrestore(&ide_lock, flags);
+ schedule_timeout(HZ/10);
+ }
+ return FAILED;
}
-int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags)
+int idescsi_reset (Scsi_Cmnd *cmd)
{
- return SCSI_RESET_SUCCESS;
+ unsigned long flags;
+ struct request *req;
+ ide_drive_t *drive = idescsi_drives[cmd->target];
+
+ printk (KERN_ERR "ide-scsi: reset called for %lu\n", cmd->serial_number);
+ /* first null the handler for the drive and let any process
+ * doing IO (on another CPU) run to (partial) completion
+ * the lock prevents processing new requests */
+ spin_lock_irqsave(&ide_lock, flags);
+ while (HWGROUP(drive)->handler) {
+ HWGROUP(drive)->handler = NULL;
+ schedule_timeout(1);
+ }
+ /* now nuke the drive queue */
+ while ((req = elv_next_request(&drive->queue))) {
+ blkdev_dequeue_request(req);
+ end_that_request_last(req);
+ }
+ /* FIXME - this will probably leak memory */
+ HWGROUP(drive)->rq = NULL;
+ if (drive->driver_data) ((idescsi_scsi_t *)drive->driver_data)->pc = NULL;
+ spin_unlock_irqrestore(&ide_lock, flags);
+ /* finally, reset the drive (and its partner on the bus...) */
+ ide_do_reset (drive);
+ return SUCCESS;
}
static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev,
@@ -859,32 +937,69 @@ static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev,
}
static Scsi_Host_Template idescsi_template = {
- .module = THIS_MODULE,
- .name = "idescsi",
- .detect = idescsi_detect,
- .release = idescsi_release,
- .info = idescsi_info,
- .ioctl = idescsi_ioctl,
- .queuecommand = idescsi_queue,
- .bios_param = idescsi_bios,
- .can_queue = 10,
- .this_id = -1,
- .sg_tablesize = 256,
- .cmd_per_lun = 5,
- .use_clustering = DISABLE_CLUSTERING,
- .emulated = 1,
+ .module = THIS_MODULE,
+ .name = "idescsi",
+ .info = idescsi_info,
+ .slave_configure = idescsi_slave_configure,
+ .ioctl = idescsi_ioctl,
+ .queuecommand = idescsi_queue,
+ .eh_abort_handler = idescsi_abort,
+ .eh_device_reset_handler = idescsi_reset,
+ .bios_param = idescsi_bios,
+ .can_queue = 40,
+ .this_id = -1,
+ .sg_tablesize = 256,
+ .cmd_per_lun = 5,
+ .max_sectors = 128,
+ .use_clustering = DISABLE_CLUSTERING,
+ .emulated = 1,
+};
+
+static struct Scsi_Host *idescsi_host;
+
+static struct device idescsi_primary = {
+ .name = "Ide-scsi Parent",
+ .bus_id = "ide-scsi",
+};
+static struct bus_type idescsi_emu_bus = {
+ .name = "ide-scsi",
};
static int __init init_idescsi_module(void)
{
+ int id;
+ int last_lun = 0;
+
ide_register_driver(&idescsi_driver);
- scsi_register_host(&idescsi_template);
+ device_register(&idescsi_primary);
+ bus_register (&idescsi_emu_bus);
+ idescsi_template.proc_name = "ide-scsi";
+ idescsi_host = scsi_register(&idescsi_template, 0);
+ if(idescsi_host == NULL)
+ return 1;
+
+ for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++)
+ last_lun = IDE_MAX(last_lun, idescsi_drives[id]->last_lun);
+ idescsi_host->max_id = id;
+ idescsi_host->max_lun = last_lun + 1;
+ scsi_add_host(idescsi_host, &idescsi_primary);
return 0;
}
static void __exit exit_idescsi_module(void)
{
- scsi_unregister_host(&idescsi_template);
+ ide_drive_t *drive;
+ int id;
+
+ scsi_remove_host(idescsi_host);
+ for (id = 0; id < MAX_HWIFS * MAX_DRIVES; id++) {
+ drive = idescsi_drives[id];
+ if (drive)
+ DRIVER(drive)->busy--;
+ }
+ scsi_unregister (idescsi_host);
+ device_unregister(&idescsi_primary);
+ bus_unregister (&idescsi_emu_bus);
ide_unregister_driver(&idescsi_driver);
}
diff --git a/drivers/scsi/lasi700.c b/drivers/scsi/lasi700.c
index 1f529b2e7241..20192e4358aa 100644
--- a/drivers/scsi/lasi700.c
+++ b/drivers/scsi/lasi700.c
@@ -114,7 +114,11 @@ static struct parisc_device_id lasi700_scsi_tbl[] = {
MODULE_DEVICE_TABLE(parisc, lasi700_scsi_tbl);
-static struct parisc_driver lasi700_driver = LASI700_DRIVER;
+static struct parisc_driver lasi700_driver = {
+ .name = "Lasi SCSI",
+ .id_table = lasi700_scsi_tbl,
+ .probe = lasi700_driver_callback,
+};
static int __init
lasi700_detect(Scsi_Host_Template *tpnt)
@@ -145,6 +149,7 @@ lasi700_driver_callback(struct parisc_device *dev)
} else {
driver_name = "lasi710";
}
+ snprintf(dev->dev.name, sizeof(dev->dev.name), "%s", driver_name);
if(hostdata == NULL) {
printk(KERN_ERR "%s: Failed to allocate host data\n",
driver_name);
@@ -168,12 +173,14 @@ lasi700_driver_callback(struct parisc_device *dev)
hostdata->chip710 = 1;
hostdata->dmode_extra = DMODE_FC2;
}
- hostdata->pci_dev = ccio_get_fake(dev);
+ hostdata->dev = &dev->dev;
+ dma_set_mask(&dev->dev, 0xffffffffUL);
if((host = NCR_700_detect(host_tpnt, hostdata)) == NULL) {
kfree(hostdata);
release_mem_region(host->base, 64);
return 1;
}
+ scsi_set_device(host, &dev->dev);
host->irq = dev->irq;
if(request_irq(dev->irq, NCR_700_intr, SA_SHIRQ, driver_name, host)) {
printk(KERN_ERR "%s: irq problem, detaching\n",
diff --git a/drivers/scsi/lasi700.h b/drivers/scsi/lasi700.h
index bf3f237edb3b..02d075d38344 100644
--- a/drivers/scsi/lasi700.h
+++ b/drivers/scsi/lasi700.h
@@ -55,12 +55,6 @@ static int lasi700_release(struct Scsi_Host *host);
.hversion_rev = HVERSION_REV_ANY_ID, \
}
-#define LASI700_DRIVER { \
- .name = "Lasi SCSI", \
- .id_table = lasi700_scsi_tbl, \
- .probe = lasi700_driver_callback,\
-}
-
#define LASI700_CLOCK 25
#define LASI710_CLOCK 40
#define LASI_SCSI_CORE_OFFSET 0x100
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 83a84dbd5b80..d5a6094c9a20 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -147,49 +147,6 @@ LIST_HEAD(scsi_dev_info_list);
extern void scsi_times_out(Scsi_Cmnd * SCpnt);
void scsi_build_commandblocks(Scsi_Device * SDpnt);
-/*
- * Function: scsi_initialize_queue()
- *
- * Purpose: Sets up the block queue for a device.
- *
- * Arguments: SDpnt - device for which we need a handler function.
- *
- * Returns: Nothing
- *
- * Lock status: No locking assumed or required.
- */
-void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt)
-{
- request_queue_t *q = SDpnt->request_queue;
-
- /*
- * tell block layer about assigned host_lock for this host
- */
- blk_init_queue(q, scsi_request_fn, SHpnt->host_lock);
-
- /* Hardware imposed limit. */
- blk_queue_max_hw_segments(q, SHpnt->sg_tablesize);
-
- /*
- * scsi_alloc_sgtable max
- */
- blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);
-
- if(!SHpnt->max_sectors)
- /* driver imposes no hard sector transfer limit.
- * start at machine infinity initially */
- SHpnt->max_sectors = SCSI_DEFAULT_MAX_SECTORS;
-
- /* FIXME: we should also adjust this limit later on
- * after we know what the device capabilities are */
- blk_queue_max_sectors(q, SHpnt->max_sectors);
-
- if (!SHpnt->use_clustering)
- clear_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
-
- blk_queue_prep_rq(q, scsi_prep_fn);
-}
-
#ifdef MODULE
MODULE_PARM(scsi_logging_level, "i");
MODULE_PARM_DESC(scsi_logging_level, "SCSI logging level; should be zero or nonzero");
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
index 8f6496f67cd1..64051a69c131 100644
--- a/drivers/scsi/scsi.h
+++ b/drivers/scsi/scsi.h
@@ -511,6 +511,7 @@ static inline void scsi_proc_host_rm(struct Scsi_Host *);
*/
extern int scsi_add_single_device(uint, uint, uint, uint);
extern int scsi_remove_single_device(uint, uint, uint, uint);
+extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *);
/*
* Prototypes for functions in constants.c
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index fba72df86df3..0c37f1329970 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -20,6 +20,8 @@
* add timers for delayed responses [20020721]
* Patrick Mansfield <patmans@us.ibm.com> max_luns+scsi_level [20021031]
* Mike Anderson <andmike@us.ibm.com> sysfs work [20021118]
+ * dpg: change style of boot options to "scsi_debug.num_devs=2" and
+ * module options to "modprobe scsi_debug num_devs=2" [20021221]
*/
#include <linux/config.h>
@@ -37,6 +39,7 @@
#include <linux/proc_fs.h>
#include <linux/smp_lock.h>
#include <linux/vmalloc.h>
+#include <linux/moduleparam.h>
#include <linux/blk.h>
#include "scsi.h"
@@ -51,7 +54,7 @@
#include "scsi_debug.h"
-static const char * scsi_debug_version_str = "Version: 1.66 (20021205)";
+static const char * scsi_debug_version_str = "Version: 1.67 (20021221)";
#ifndef SCSI_CMD_READ_16
#define SCSI_CMD_READ_16 0x88
@@ -63,7 +66,7 @@ static const char * scsi_debug_version_str = "Version: 1.66 (20021205)";
#define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
/* Default values for driver parameters */
-#define DEF_NR_FAKE_DEVS 1
+#define DEF_NUM_DEVS 1
#define DEF_DEV_SIZE_MB 8
#define DEF_EVERY_NTH 100
#define DEF_DELAY 1
@@ -82,7 +85,7 @@ static const char * scsi_debug_version_str = "Version: 1.66 (20021205)";
#define OPT_MEDIUM_ERR_ADDR 0x1234
static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
-static int scsi_debug_num_devs = DEF_NR_FAKE_DEVS;
+static int scsi_debug_num_devs = DEF_NUM_DEVS;
static int scsi_debug_opts = DEF_OPTS;
static int scsi_debug_every_nth = DEF_EVERY_NTH;
static int scsi_debug_cmnd_count = 0;
@@ -1100,160 +1103,33 @@ static int schedule_resp(struct scsi_cmnd * cmnd,
}
}
-#ifndef MODULE
-static int __init num_devs_setup(char *str)
-{
- int tmp;
-
- if (get_option(&str, &tmp) == 1) {
- if (tmp > 0)
- scsi_debug_num_devs = tmp;
- return 1;
- } else {
- printk(KERN_INFO "scsi_debug_num_devs: usage scsi_debug_num_devs=<n> "
- "(<n> can be from 1 to around 2000)\n");
- return 0;
- }
-}
-__setup("scsi_debug_num_devs=", num_devs_setup);
-
-static int __init max_luns_setup(char *str)
-{
- int tmp;
-
- if (get_option(&str, &tmp) == 1) {
- if (tmp > 0)
- scsi_debug_max_luns = tmp;
- return 1;
- } else {
- printk(KERN_INFO "scsi_debug_max_luns: usage scsi_debug_max_luns=<n> "
- "(<n> is a postive integer (def=2))\n");
- return 0;
- }
-}
-__setup("scsi_debug_max_luns=", max_luns_setup);
-
-static int __init scsi_level_setup(char *str)
-{
- int tmp;
-
- if (get_option(&str, &tmp) == 1) {
- if (tmp > 0)
- scsi_debug_scsi_level = tmp;
- return 1;
- } else {
- printk(KERN_INFO "scsi_debug_scsi_level: usage "
- "scsi_debug_scsi_level=<n> (<n> is 1..4 (def=3))\n");
- return 0;
- }
-}
-__setup("scsi_debug_scsi_level=", scsi_level_setup);
-
-static int __init dev_size_mb_setup(char *str)
-{
- int tmp;
-
- if (get_option(&str, &tmp) == 1) {
- if (tmp > 0)
- scsi_debug_dev_size_mb = tmp;
- return 1;
- } else {
- printk(KERN_INFO "scsi_debug_dev_size_mb: usage "
- "scsi_debug_dev_size_mb=<n>\n"
- " (<n> is number of MB ram shared by all devs\n");
- return 0;
- }
-}
-__setup("scsi_debug_dev_size_mb=", dev_size_mb_setup);
-
-static int __init opts_setup(char *str)
-{
- int tmp;
-
- if (get_option(&str, &tmp) == 1) {
- if (tmp > 0)
- scsi_debug_opts = tmp;
- return 1;
- } else {
- printk(KERN_INFO "scsi_debug_opts: usage "
- "scsi_debug_opts=<n>\n"
- " (1->noise, 2->medium_error, 4->... (can be or-ed)\n");
- return 0;
- }
-}
-__setup("scsi_debug_opts=", opts_setup);
-
-static int __init every_nth_setup(char *str)
-{
- int tmp;
-
- if (get_option(&str, &tmp) == 1) {
- if (tmp > 0)
- scsi_debug_every_nth = tmp;
- return 1;
- } else {
- printk(KERN_INFO "scsi_debug_every_nth: usage "
- "scsi_debug_every_nth=<n>\n"
- " timeout every nth command (when ...)\n");
- return 0;
- }
-}
-__setup("scsi_debug_every_nth=", every_nth_setup);
-
-static int __init delay_setup(char *str)
-{
- int tmp;
-
- if (get_option(&str, &tmp) == 1) {
- scsi_debug_delay = tmp;
- return 1;
- } else {
- printk(KERN_INFO "scsi_debug_delay: usage "
- "scsi_debug_delay=<n>\n"
- " delay response <n> jiffies\n");
- return 0;
- }
-}
-__setup("scsi_debug_delay=", delay_setup);
-
-static int __init add_host_setup(char *str)
-{
- int tmp;
-
- if (get_option(&str, &tmp) == 1) {
- scsi_debug_add_host = tmp;
- return 1;
- } else {
- printk(KERN_INFO "scsi_debug_add_host: usage "
- "scsi_debug_add_host=<n>\n"
- " <n> 0..127 (default 1)\n");
- return 0;
- }
-}
-__setup("scsi_debug_add_host=", add_host_setup);
-
-#endif
+/* Set 'perm' (4th argument) to 0 to disable module_param's definition
+ * of sysfs parameters (which module_param doesn't yet support).
+ * Sysfs parameters defined explicitly below.
+ */
+module_param_named(num_devs, scsi_debug_num_devs, int, 0);
+module_param_named(max_luns, scsi_debug_max_luns, int, 0);
+module_param_named(scsi_level, scsi_debug_scsi_level, int, 0);
+module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, 0);
+module_param_named(opts, scsi_debug_opts, int, 0); /* perm=0644 */
+module_param_named(every_nth, scsi_debug_every_nth, int, 0);
+module_param_named(delay, scsi_debug_delay, int, 0); /* perm=0644 */
+module_param_named(add_host, scsi_debug_add_host, int, 0); /* perm=0644 */
MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
MODULE_DESCRIPTION("SCSI debug adapter driver");
-MODULE_PARM(scsi_debug_num_devs, "i");
-MODULE_PARM_DESC(scsi_debug_num_devs, "number of SCSI devices to simulate");
-MODULE_PARM(scsi_debug_max_luns, "i");
-MODULE_PARM_DESC(scsi_debug_max_luns, "number of SCSI LUNs per target to simulate");
-MODULE_PARM(scsi_debug_scsi_level, "i");
-MODULE_PARM_DESC(scsi_debug_scsi_level, "SCSI level to simulate");
-MODULE_PARM(scsi_debug_dev_size_mb, "i");
-MODULE_PARM_DESC(scsi_debug_dev_size_mb, "size in MB of ram shared by devs");
-MODULE_PARM(scsi_debug_opts, "i");
-MODULE_PARM_DESC(scsi_debug_opts, "1->noise, 2->medium_error, 4->...");
-MODULE_PARM(scsi_debug_every_nth, "i");
-MODULE_PARM_DESC(scsi_debug_every_nth, "timeout every nth command(def=100)");
-MODULE_PARM(scsi_debug_delay, "i");
-MODULE_PARM_DESC(scsi_debug_delay, "# of jiffies to delay response(def=1)");
-MODULE_PARM(scsi_debug_add_host, "i");
-MODULE_PARM_DESC(scsi_debug_add_host, "0..127 hosts allowed(def=1)");
MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(num_devs, "number of SCSI devices to simulate");
+MODULE_PARM_DESC(max_luns, "number of SCSI LUNs per target to simulate");
+MODULE_PARM_DESC(scsi_level, "SCSI level to simulate");
+MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs");
+MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->...");
+MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)");
+MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
+MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
+
+
static char sdebug_info[256];
static const char * scsi_debug_info(struct Scsi_Host * shp)
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 4a02c735dbf2..ac07ad6a954f 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -28,7 +28,6 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
-
#include <linux/blk.h>
#include "scsi.h"
@@ -365,34 +364,60 @@ static void print_inquiry(unsigned char *inq_result)
printk("\n");
}
-/**
- * scsi_initialize_merge_fn() -ƣinitialize merge function for a host
- * @sd: host descriptor
- */
-static void scsi_initialize_merge_fn(struct scsi_device *sd)
+u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost)
{
- request_queue_t *q = sd->request_queue;
- struct Scsi_Host *sh = sd->host;
- struct device *dev = scsi_get_device(sh);
- u64 bounce_limit;
-
- if (sh->highmem_io) {
- if (dev && dev->dma_mask && PCI_DMA_BUS_IS_PHYS) {
- bounce_limit = *dev->dma_mask;
- } else {
- /*
- * Platforms with virtual-DMA translation
- * hardware have no practical limit.
- */
- bounce_limit = BLK_BOUNCE_ANY;
- }
- } else if (sh->unchecked_isa_dma) {
- bounce_limit = BLK_BOUNCE_ISA;
- } else {
- bounce_limit = BLK_BOUNCE_HIGH;
+ if (shost->highmem_io) {
+ struct device *host_dev = scsi_get_device(shost);
+
+ if (PCI_DMA_BUS_IS_PHYS && host_dev && host_dev->dma_mask)
+ return *host_dev->dma_mask;
+
+ /*
+ * Platforms with virtual-DMA translation
+ * hardware have no practical limit.
+ */
+ return BLK_BOUNCE_ANY;
+ } else if (shost->unchecked_isa_dma)
+ return BLK_BOUNCE_ISA;
+
+ return BLK_BOUNCE_HIGH;
+}
+
+static request_queue_t *scsi_alloc_queue(struct Scsi_Host *shost)
+{
+ request_queue_t *q;
+
+ q = kmalloc(sizeof(*q), GFP_ATOMIC);
+ if (!q)
+ return NULL;
+ memset(q, 0, sizeof(*q));
+
+ if (!shost->max_sectors) {
+ /*
+ * Driver imposes no hard sector transfer limit.
+ * start at machine infinity initially.
+ */
+ shost->max_sectors = SCSI_DEFAULT_MAX_SECTORS;
}
- blk_queue_bounce_limit(q, bounce_limit);
+ blk_init_queue(q, scsi_request_fn, shost->host_lock);
+ blk_queue_prep_rq(q, scsi_prep_fn);
+
+ blk_queue_max_hw_segments(q, shost->sg_tablesize);
+ blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);
+ blk_queue_max_sectors(q, shost->max_sectors);
+ blk_queue_bounce_limit(q, scsi_calculate_bounce_limit(shost));
+
+ if (!shost->use_clustering)
+ clear_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
+
+ return q;
+}
+
+static void scsi_free_queue(request_queue_t *q)
+{
+ blk_cleanup_queue(q);
+ kfree(q);
}
/**
@@ -435,19 +460,15 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
*/
sdev->borken = 1;
- if(!q || *q == NULL) {
- sdev->request_queue = kmalloc(sizeof(struct request_queue), GFP_ATOMIC);
- if(sdev->request_queue == NULL) {
+ if (!q || *q == NULL) {
+ sdev->request_queue = scsi_alloc_queue(shost);
+ if (!sdev->request_queue)
goto out_bail;
- }
- memset(sdev->request_queue, 0,
- sizeof(struct request_queue));
- scsi_initialize_queue(sdev, shost);
- scsi_initialize_merge_fn(sdev);
} else {
sdev->request_queue = *q;
*q = NULL;
}
+
sdev->request_queue->queuedata = sdev;
scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
scsi_build_commandblocks(sdev);
@@ -488,13 +509,12 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
}
out_bail:
printk(ALLOC_FAILURE_MSG, __FUNCTION__);
- if(q && sdev->request_queue) {
+ if (q && sdev->request_queue) {
*q = sdev->request_queue;
sdev->request_queue = NULL;
- } else if(sdev->request_queue) {
- blk_cleanup_queue(sdev->request_queue);
- kfree(sdev->request_queue);
- }
+ } else if (sdev->request_queue)
+ scsi_free_queue(sdev->request_queue);
+
scsi_release_commandblocks(sdev);
kfree(sdev);
return NULL;
@@ -513,14 +533,12 @@ static void scsi_free_sdev(struct scsi_device *sdev)
list_del(&sdev->siblings);
list_del(&sdev->same_target_siblings);
- if(sdev->request_queue != NULL) {
- blk_cleanup_queue(sdev->request_queue);
- kfree(sdev->request_queue);
- }
+ if (sdev->request_queue)
+ scsi_free_queue(sdev->request_queue);
scsi_release_commandblocks(sdev);
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
- if (sdev->inquiry != NULL)
+ if (sdev->inquiry)
kfree(sdev->inquiry);
kfree(sdev);
}
@@ -1946,10 +1964,9 @@ void scsi_scan_host(struct Scsi_Host *shost)
scsi_scan_target(shost, &q, channel, order_id);
}
}
- if(q) {
- blk_cleanup_queue(q);
- kfree(q);
- }
+
+ if (q)
+ scsi_free_queue(q);
}
void scsi_forget_host(struct Scsi_Host *shost)
diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
index 85de9b673e33..c3f938e2a99c 100644
--- a/drivers/scsi/scsi_syms.c
+++ b/drivers/scsi/scsi_syms.c
@@ -98,6 +98,11 @@ EXPORT_SYMBOL(scsi_host_put);
EXPORT_SYMBOL(scsi_device_types);
/*
+ * This is for st to find the bounce limit
+ */
+EXPORT_SYMBOL(scsi_calculate_bounce_limit);
+
+/*
* Externalize timers so that HBAs can safely start/restart commands.
*/
extern void scsi_add_timer(Scsi_Cmnd *, int, void ((*) (Scsi_Cmnd *)));
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 8dc808d1e727..b463de9d1367 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -3764,21 +3764,9 @@ static int st_attach(Scsi_Device * SDp)
tpnt->nbr_partitions = 0;
tpnt->timeout = ST_TIMEOUT;
tpnt->long_timeout = ST_LONG_TIMEOUT;
-
tpnt->try_dio = try_direct_io && !SDp->host->unchecked_isa_dma;
- bounce_limit = BLK_BOUNCE_HIGH; /* Borrowed from scsi_merge.c */
- if (SDp->host->highmem_io) {
- struct device *dev = scsi_get_device(SDp->host);
- if (!PCI_DMA_BUS_IS_PHYS)
- /* Platforms with virtual-DMA translation
- * hardware have no practical limit.
- */
- bounce_limit = BLK_BOUNCE_ANY;
- else if (dev && dev->dma_mask)
- bounce_limit = *dev->dma_mask;
- } else if (SDp->host->unchecked_isa_dma)
- bounce_limit = BLK_BOUNCE_ISA;
- bounce_limit >>= PAGE_SHIFT;
+
+ bounce_limit = scsi_calculate_bounce_limit(SDp->host) >> PAGE_SHIFT;
if (bounce_limit > ULONG_MAX)
bounce_limit = ULONG_MAX;
tpnt->max_pfn = bounce_limit;