diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2002-12-23 04:31:37 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2002-12-23 04:31:37 -0800 |
| commit | c4e3c771b5bd9f0ed5341a2743e1a0ac6bf4d6fa (patch) | |
| tree | 938b09002fccc7b571e72abb17f016491b47a8d0 | |
| parent | a27aaaf7fba7843686d66af56d9b0b0294f44d4b (diff) | |
| parent | 91076e502fb8684d1c7269a72edc84dd651f25e8 (diff) | |
Merge bk://linux-scsi.bkbits.net/scsi-for-linus-2.5
into home.transmeta.com:/home/torvalds/v2.5/linux
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 *)≻ - - 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, ¤t_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 *)≻ - 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, ¯o_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, ¯o_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(®isters); 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(®isters, 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, ®isters, links) { - memcpy(&cursym, data.data, sizeof(cursym)); - switch(cursym->type) { - case REGISTER: - case SCBLOC: - case SRAMLOC: - symlist_add(®isters, 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(®isters, regname); - SLIST_INSERT_AFTER(regnode, curnode, links); - } + regnode = SLIST_FIRST(&curnode->symbol->info.finfo->symrefs); + regname = regnode->symbol->name; + regnode = symlist_search(®isters, 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(®isters, regname); - SLIST_INSERT_AFTER(regnode, curnode, links); - } + regname = curnode->symbol->info.ainfo->parent->name; + regnode = symlist_search(®isters, 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(®isters) != NULL) { - symbol_node_t *curnode; - u_int8_t value; - char *tab_str; - char *tab_str2; - - curnode = SLIST_FIRST(®isters); - SLIST_REMOVE_HEAD(®isters, 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(®isters) != NULL) { + symbol_node_t *curnode; + u_int value; + char *tab_str; + char *tab_str2; + + curnode = SLIST_FIRST(®isters); + SLIST_REMOVE_HEAD(®isters, 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; |
